Topolo Docs Topolo Platform
Open workspace

Approve message batch

Approve every currently pending message in one discovered campaign-step batch.

Outcome

Approve every currently pending message in one discovered campaign-step batch.

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

  • Read approvals.list filtered to the discovered campaign. Group the exact pending rows by campaignId, stepId and batchDate, which is the UTC render date.
  • Review every subject and body in that batch. Read campaigns.policy.get and require approvalMode=per_batch before planning. Do not substitute today’s date for the returned batchDate.
topolo whoami --json
topolo actions get app_topolo_engage.approvals.approve_batch --json
topolo workspaces list topolo-engage --json

Input fields

FieldRequiredMeaning and constraints
campaignIdYesRequired campaign identifier copied from the reviewed queue.
stepIdYesRequired step identifier copied from the same queue rows.
batchDateYesRequired YYYY-MM-DD UTC render date copied from those rows; not the intended send date.

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.approvals.approve_batch --data @payload.json --json
topolo actions plan app_topolo_engage.approvals.approve_batch --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.approvals.approve_batch --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 approvals.approve_batch · HTTP 200

Action input

{
  "campaignId": "eng_cmp_1",
  "stepId": "eng_stp_1",
  "batchDate": "2026-10-05"
}

Actual local response

{
  "approved": 0,
  "messageIds": []
}

Source test: message approval > per_batch: approves every pending message of one step and render date at once. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.

Captured approvals.approve_batch · HTTP 200

Action input

{
  "campaignId": "eng_cmp_1",
  "stepId": "eng_stp_1",
  "batchDate": "2026-10-06"
}

Actual local response

{
  "approved": 2,
  "messageIds": [
    "eng_msg_1",
    "eng_msg_2"
  ]
}

Source test: message approval > per_batch: approves every pending message of one step and render date at once. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.

Effects and response meaning

  • Approves matching pending messages and records the current policy version and an audit entry. Returns approved count and messageIds for rows actually changed.
  • Approving a batch can enable external sends through the runner. It does not bypass other holds or prove provider delivery.
  • A successful approved=0 with messageIds=[] means no matching pending rows were changed; it can reflect an empty, mismatched or already-processed batch.

Verify the result

  • Check approved equals messageIds.length and retain the returned IDs. Read enrollments.messages.list for each enrollment retained from the reviewed queue; match those IDs and inspect approvalState and delivery state.
  • Re-read approvals.list for remaining pending rows. A zero count or an empty queue is not proof that a prior approval or send succeeded.

Recover

After an uncertain response, reconcile every reviewed message through its enrollment history before repeating a batch decision. Repeating the same tuple can approve newly pending rows in that batch, so it is not an exact retry of a fixed ID set. No undo-batch or recall action is declared; review any stop separately.

Refused responses

Captured approvals.approve_batch · HTTP 409

Action input

{
  "campaignId": "eng_cmp_1",
  "stepId": "eng_stp_1",
  "batchDate": "2026-10-06"
}

Actual local response

{
  "error": {
    "code": "state_conflict",
    "message": "Batch approval needs the campaign policy to be per_batch."
  }
}

Source test: message approval > refuses batch approval unless the policy is per_batch, and for an unknown campaign. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.

Captured approvals.approve_batch · HTTP 404

Action input

{
  "campaignId": "eng_cmp_9",
  "stepId": "eng_stp_1",
  "batchDate": "2026-10-06"
}

Actual local response

{
  "error": {
    "code": "not_found",
    "message": "Campaign was not found in this workspace."
  }
}

Source test: message approval > refuses batch approval unless the policy is per_batch, and for an unknown campaign. Actual route handlers with SQLite repositories; synthetic identity/workspace grants and provider ports.

Error recovery

  • HTTP 403 permission_denied: Stop and inspect the action permission in the current credential; do not broaden permissions or switch contexts automatically.
  • HTTP 422 invalid_action_input: Correct the input against the current schema and validate again before planning.
  • HTTP 409 state_conflict: Read current message history and campaign policy. A message must still be scheduled and pending_approval; batch approval also requires the per_batch policy. Never loop on an already-applied or invalid decision.
  • HTTP 404 not_found: Rediscover the target using approvals.list and retained enrollment message history in the same workspace. Batch approval may instead refer to an unknown campaign; verify it with campaigns.get.

Evidence and limits

  • contracts/autonomy.mjs

  • contracts/sending.mjs

  • apps/api/src/autonomy/approvals-handlers.ts

  • apps/api/src/autonomy/approvals-repository.ts

  • apps/api/src/autonomy/policy-service.ts

  • apps/api/test/autonomy.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.