Outcome
Find outreach campaigns and their actual identifiers in the selected Engage workspace.
Before you start
- Verify the environment, credential, authorized organization and the published action permission workspace:read.
- Discover an accessible Engage workspace and bind resourceType=workspace and its resourceId separately from action input. Fixture identifiers grant no access.
Discover the target
- Select an authorized canonical Engage workspace separately from input. No campaign identifier is needed.
- Use status=archived to find archived campaigns; the default query excludes them. Other status values select exactly that status.
topolo whoami --json
topolo actions get app_topolo_engage.campaigns.list --json
topolo workspaces list topolo-engage --json
Input fields
| Field | Required | Meaning and constraints |
|---|---|---|
status | No | Optional exact status: draft, active, paused, completed or archived. Omit to exclude archived campaigns. |
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.campaigns.list --data @payload.json --json
topolo actions plan app_topolo_engage.campaigns.list --resource-type workspace --resource-id "$ENGAGE_WORKSPACE_ID" --data @payload.json --json
Execute the unchanged validated read in the selected workspace.
: "${ENGAGE_WORKSPACE_ID:?Set ENGAGE_WORKSPACE_ID to your selected workspace identifier}"
topolo actions call app_topolo_engage.campaigns.list --resource-type workspace --resource-id "$ENGAGE_WORKSPACE_ID" --data @payload.json --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 campaigns.list · HTTP 200
Action input
{}
Actual local response
{
"campaigns": [
{
"id": "eng_cmp_1",
"name": "Q4 outreach",
"goal": "Book demos",
"channel": "email",
"status": "draft",
"isTest": false,
"createdBy": "user_1",
"updatedBy": "user_1",
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
]
}
Source test: campaign routes > creates, lists, reads, edits and transitions a campaign (happy path). Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured campaigns.list · HTTP 200
Action input
{}
Actual local response
{
"campaigns": []
}
Source test: campaign routes > returns 404 for a campaign that lives in another workspace. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured campaigns.list · HTTP 200
Action input
{}
Actual local response
{
"campaigns": [
{
"id": "eng_cmp_1",
"name": "x",
"goal": null,
"channel": "email",
"status": "draft",
"isTest": false,
"createdBy": "user_1",
"updatedBy": "user_1",
"createdAt": "2026-10-01T00:00:00.000Z",
"updatedAt": "2026-10-01T00:00:00.000Z",
"archivedAt": null
}
]
}
Source test: campaign routes > falls back to the first verified workspace when none is marked default. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Effects and response meaning
- Reads workspace-scoped campaign rows, ordered by createdAt descending and then id descending. The response is campaigns[], including an empty array when no rows match.
- This action does not paginate or change campaign state. An empty result does not prove the workspace has no archived campaigns.
Verify the result
- Check the requested status against each returned campaigns[].status. Use campaigns[].id as the input id for campaigns.get; retain the same workspace binding.
Recover
Retry this read in the same verified resource context after a transient failure. If a campaign is missing, inspect status=archived before concluding it was removed. Never substitute a guessed identifier.
Refused responses
Captured campaigns.list · HTTP 403
Action input
{}
Actual local response
{
"error": {
"code": "permission_denied",
"message": "The credential lacks workspace:read.",
"details": {
"requiredPermission": "workspace:read"
}
}
}
Source test: campaign routes > refuses writes without workspace:write (403) but allows reads. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured campaigns.list · HTTP 403
Action input
{}
Actual local response
{
"error": {
"code": "organization_required",
"message": "This action needs an organization context."
}
}
Source test: campaign routes > requires an organization context. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured campaigns.list · HTTP 403
Action input
{}
Actual local response
{
"error": "workspace_forbidden"
}
Source test: campaign routes > denies a workspace the caller holds no grant for. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured campaigns.list · HTTP 422
Refused input; do not execute
{
"status": "sent"
}
Actual local response
{
"error": {
"code": "invalid_action_input",
"message": "Input for campaigns.list is invalid.",
"details": [
{
"code": "invalid_value",
"values": [
"draft",
"active",
"paused",
"completed",
"archived"
],
"path": [
"status"
],
"message": "Invalid option: expected one of \"draft\"|\"active\"|\"paused\"|\"completed\"|\"archived\""
}
]
}
}
Source test: campaign routes > returns 422 for input the contract refuses. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured campaigns.list · HTTP 409
Action input
{}
Actual local response
{
"error": {
"code": "workspace_required",
"message": "Create an Engage workspace before using this action."
}
}
Source test: campaign routes > refuses when the caller has no workspace at all. 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 403
organization_required: Establish the intended authorized organization context before another request. Organization identity is not an action-input field. - HTTP 403
workspace_forbidden: Discover an accessible Engage workspace with the current credential and bind that resource explicitly; do not retry the denied fixture identifier. - HTTP 422
invalid_action_input: Correct the request against the current input schema and validate it again before planning. - HTTP 409
workspace_required: Discover an accessible Engage workspace. If none exists, workspace creation is a separate approved operation; this campaign action cannot create one.
Related actions
Evidence and limits
-
contracts/campaigns.mjs -
apps/api/src/campaigns/campaigns-routes.ts -
apps/api/src/campaigns/campaigns-handlers.ts -
apps/api/src/campaigns/campaigns-domain.ts -
apps/api/src/campaigns/campaigns-repository.ts -
apps/api/test/campaigns-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.