Before you start
- Create a Write Workspace key for the intended workspace, with an active grant covering
propose-actions. This grant is required for Actions reads too; a normal Read key is insufficient. Older Admin keys still need the applicable grant and current role. - Connect an active Meta ad account owned by that workspace. Confirm its scope with
get_account_state. - Obtain the workspace’s integer ID and Plainrouter’s internal integer
account_id. The latter is different from Meta’s external account ID. The examples use workspace42and account91; replace both. - Load the key into
PLAINROUTER_WORKSPACE_KEYthrough your secret manager. A Signals Server secret, Management key, OAuth management credential, or browser session cannot replace it.
Authorization: Bearer <workspace-key> and Accept: application/json. For POST requests, also send Content-Type: application/json.
Select the account
Supplyaccount_id as a query parameter on GET requests and in the JSON body on POST. You may omit it when there is only one eligible active account. Multiple eligible accounts require an explicit selection. An older account-bound key accepts only its bound account. The workspace in the URL must match the key’s workspace. See account selection.
Endpoints
{actionBatch} and {action} are the ULIDs returned by Plainrouter, not Meta object IDs. All successful reads return HTTP 200.
List and inspect actions
The response has a
data array and meta.current_page, meta.last_page, meta.per_page, and meta.total. Continue through the returned pages; there is no configurable per_page parameter.
Each action contains id, batch_id, workspace_id, type, target_entity_type, target_entity_id, target_entity_name, params, rationale, status, policy_decision, and policy_reasons. Read responses use these flat target fields; proposal input uses a nested target_entity object.
To inspect a returned action, request /actions/{action}?account_id=91. The response contains one action under data. To inspect its batch, request /action-batches/{actionBatch}?account_id=91; data contains the batch’s id, workspace and platform-account IDs, status, policy result and reasons, rationale, idempotency key, and actions array. Confirm the scope and read both batch and action statuses.
Read the current policy
data object includes id, workspace_id, execution_mode, max_spend_delta_percent, hard_account_daily_cap_minor, protected_entities, quiet_hours_start, quiet_hours_end, protect_learning_phase, outcome_check_after_hours, and anomaly_threshold_percent.
If no policy has been saved, the read returns effective defaults with id: null; it does not create a policy. This is the current policy, not the frozen mode or policy evidence from an earlier proposal. Review policy and safety before interpreting execution permissions.
Retrieve a decision receipt
Request/actions/{action}/decision-receipt?account_id=91 with the same headers. The response contains these top-level fields, without a data wrapper:
A receipt may not exist yet, and a receipt can describe failure. Check its terminal status and verification evidence before reporting success. This endpoint exposes one scoped receipt, not the complete workspace audit export. See receipt interpretation and integrity limits.
Submit a governed proposal
POST uses the same proposal input as MCP:actions (1–25 objects), rationale, idempotency_key, evidence (1–3 declarations), target_source: "human_supplied", and account_id when required. Each action needs its typed parameters, selected target, and rationale. An array of action-name strings is not a valid proposal.
Prepare proposal.json using the documented action shapes and evidence rules. For creative work, follow the paused-variant walkthrough. Do not invent target IDs, asset IDs, or evidence. Submission also requires actions.propose; upload and creative-duplication proposals require creative.write.
201; an idempotent repeat returns 200 with the existing result. Preserve the original idempotency key for the same intent after a timeout. The response includes batch and action IDs, scope, policy reasons, approval requirements, status, and inbox_url; approval_queue_url remains a compatibility alias.
Send the reviewer to Inbox and use the reads above to follow the result. These routes do not approve or reject proposals, edit policy, manage keys, or bypass human review. Suggest only approval ends without execution. A successful proposal response is not proof of a provider change or Landed status.
The public MCP server card publishes the shared proposal schema. A schema-accepted action type alone does not establish that its execution workflow is available.
Troubleshoot a request
The equivalent five MCP read tools use the same scoped records and output shapes. MCP tool errors follow the MCP protocol rather than this HTTP status table.