Topolo Docs Topolo Platform
Open workspace

Update outreach campaign

Edit campaign name, goal or the test flag while retaining its identifier and status.

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

FieldRequiredMeaning and constraints
idYesRequired discovered campaign identifier.
nameNoOptional trimmed replacement name, 1–120 characters.
goalNoOptional nullable replacement goal, up to 2,000 characters; null or blank clears it.
isTestNoOptional 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.

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.