Outcome
Read one discovered outreach campaign and its current state.
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
- Read campaigns.list in the same workspace and select campaigns[].id. Archived campaigns require status=archived on that discovery read.
topolo whoami --json
topolo actions get app_topolo_engage.campaigns.get --json
topolo workspaces list topolo-engage --json
Input fields
| Field | Required | Meaning and constraints |
|---|---|---|
id | Yes | Required campaign identifier returned by campaigns.list or campaigns.create; trimmed non-empty string up to 128 characters. |
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.get --data @payload.json --json
topolo actions plan app_topolo_engage.campaigns.get --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.get --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.get · HTTP 200
Action input
{
"id": "eng_cmp_1"
}
Actual local response
{
"campaign": {
"id": "eng_cmp_1",
"name": "Self-test",
"goal": null,
"channel": "email",
"status": "draft",
"isTest": false,
"createdBy": "user_1",
"updatedBy": "user_1",
"createdAt": "2026-10-06T08:30:00.000Z",
"updatedAt": "2026-10-06T08:30:00.000Z",
"archivedAt": null
}
}
Source test: campaign test flag > is set on create or update and returned on the campaign. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured campaigns.get · HTTP 200
Action input
{
"id": "eng_cmp_1"
}
Actual local response
{
"campaign": {
"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.get · HTTP 200
Action input
{
"id": "eng_cmp_1"
}
Actual local response
{
"campaign": {
"id": "eng_cmp_1",
"name": "Mine",
"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 > returns 404 for a campaign that lives in another workspace. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Effects and response meaning
- Reads a single workspace-scoped campaign at response campaign. A campaign belonging to another workspace returns not_found rather than exposing its content.
Verify the result
- Compare response campaign.id with the requested id. Retain name, goal, isTest, status, updatedAt and archivedAt before planning an edit or transition.
Recover
Retry the read after a transient failure with the same discovered id and workspace. On not_found, rediscover within that workspace rather than guessing other resource contexts.
Refused responses
Captured campaigns.get · HTTP 404
Action input
{
"id": "eng_cmp_1"
}
Actual local response
{
"error": {
"code": "not_found",
"message": "Campaign was not found in this workspace."
}
}
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.
Error recovery
- HTTP 404
not_found: Rediscover the campaign using campaigns.list in the same authorized workspace, including status=archived when appropriate. Do not guess another organization or workspace.
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.