Outcome
Edit campaign name, goal or the test flag while retaining its identifier and status.
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 campaigns[].id through campaigns.list and read campaigns.get before planning. Retain all previous editable values.
- Archived campaigns cannot be edited. Supply at least one editable field; omitted fields retain their values.
topolo whoami --json
topolo actions get app_topolo_engage.campaigns.update --json
topolo workspaces list topolo-engage --json
Input fields
| Field | Required | Meaning and constraints |
|---|---|---|
id | Yes | Required discovered campaign identifier. |
name | No | Optional trimmed replacement name, 1–120 characters. |
goal | No | Optional nullable replacement goal, up to 2,000 characters; null or blank clears it. |
isTest | No | Optional replacement test flag; changes analytics inclusion. |
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.update --data @payload.json --json
topolo actions plan app_topolo_engage.campaigns.update --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.campaigns.update --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 campaigns.update · HTTP 200
Action input
{
"id": "eng_cmp_3",
"isTest": true
}
Actual local response
{
"campaign": {
"id": "eng_cmp_3",
"name": "Q4",
"goal": null,
"channel": "email",
"status": "active",
"isTest": true,
"createdBy": "user_1",
"updatedBy": "user_1",
"createdAt": "2026-10-06T08:30:00.000Z",
"updatedAt": "2026-10-06T08:30:00.000Z",
"archivedAt": null
}
}
Source test: engagement analytics > labels a fact whose version or step is gone with an empty subject. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Effects and response meaning
- Updates only supplied name, goal and isTest, plus updatedBy and updatedAt. It retains campaign.id, channel and status.
- Setting goal=null or blank text clears the goal. isTest affects inclusion in subsequent analytics reports.
- There is no declared compare-and-swap version input; re-read before an approved correction to avoid blindly overwriting concurrent edits.
Verify the result
- Read campaigns.get with the same id and workspace. Compare every supplied editable field after normalization, and confirm omitted fields and status were retained.
Recover
After a lost response, read campaigns.get first and compare the intended fields. Do not write again if the intended state is already present. For a reviewed correction, supply only the previous editable fields being restored after checking for concurrent changes. An archived campaign cannot be restored through this action.
Refused responses
Captured campaigns.update · HTTP 404
Action input
{
"id": "eng_cmp_1",
"name": "x"
}
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.
Captured campaigns.update · HTTP 409
Action input
{
"id": "eng_cmp_1",
"name": "y"
}
Actual local response
{
"error": {
"code": "state_conflict",
"message": "Archived campaigns cannot be edited."
}
}
Source test: campaign routes > returns 409 for an invalid transition and for editing an archived campaign. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.
Captured campaigns.update · HTTP 422
Action input
{
"id": "eng_cmp_1"
}
Actual local response
{
"error": {
"code": "invalid_input",
"message": "Provide a name, a goal or the test flag to change."
}
}
Source test: campaign routes > returns 422 for input the contract refuses. 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. - HTTP 409
state_conflict: Read campaigns.get. Archived campaigns are terminal and cannot be edited; do not retry the same refused update. - HTTP 422
invalid_input: For campaign edits, supply at least one of name, goal or isTest; an id-only update is refused.
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.