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
| Field | Required | Meaning and constraints |
|---|---|---|
campaignId | Yes | Required campaign identifier copied from the reviewed queue. |
stepId | Yes | Required step identifier copied from the same queue rows. |
batchDate | Yes | Required 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.
Related actions
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.