Automated rule runs¶
An automated rule applies an operation — enrolling people, assigning content, syncing group membership — to everyone matched by a saved report. Rules run automatically on the platform's periodic sweep, but you can also trigger an out-of-cycle run through the API. This guide covers creating a rule, the trigger-and-poll flow, plus the webhook events that let you skip polling entirely.
Every endpoint in this guide lives under /automated-rules/, so your token needs the automations scope family: automations:read to list rules and runs, automations:write to create, update, reorder or trigger them.
Create a rule¶
A rule is three pieces: a saved report (segment_id — who it applies to), an operation (parameters.operation — what it does to each matched person), and a name. Three operations can be created through the API today:
parameters.operation |
What it does | Operation-specific fields |
|---|---|---|
sync_group_from_segment |
Mirrors the saved report's membership into one group | group_id |
sync_group_template_from_segment |
Derives one or more group names per person from a template, and keeps each derived group in sync | group_name_template, group_name_delimiter |
assign_content_item_to_user |
Assigns a content item to every matched person | content_item_public_id, assigner_id (optional — defaults to your API token's user), time_to_complete, time_to_complete_days_offset |
Other rule kinds are not yet creatable
Rules built from the event- and track-enrollment operations (enroll_user_on_next_event_of_event_type and friends) are fully visible and runnable through this API, but can't be created or reconfigured here yet — build those in the PlusPlus app. The exact request/response schema for every operation, creatable or not, is in the API reference.
Sync one group from an HR-driven saved report:
curl -X POST https://acme.plusplus.app/api/v2/automated-rules/ \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Sync Engineering group from HR segment",
"segment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"parameters": {
"operation": "sync_group_from_segment",
"group_id": "550e8400-e29b-41d4-a716-446655440000"
}
}'
Or assign onboarding content to everyone the report matches, with a 14-day deadline:
curl -X POST https://acme.plusplus.app/api/v2/automated-rules/ \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{
"name": "Assign the security policy to new hires",
"segment_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"parameters": {
"operation": "assign_content_item_to_user",
"content_item_public_id": "9b2d1c3e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
"time_to_complete": "custom",
"time_to_complete_days_offset": 14
}
}'
An unresolvable reference in the body — an unknown segment_id, group_id, or content_item_public_id — returns 400 (invalid_segment / invalid_parameters) and creates nothing.
Test it, then enable it¶
A new rule is disabled by default and is appended at the end of the execution order. That's deliberate: trigger an explicit run first (POST .../run/ works while disabled — see below), inspect the run's matched_users/affected_users, then flip it on for the periodic sweep:
curl -X PATCH https://acme.plusplus.app/api/v2/automated-rules/{public_id}/ \
-H "Authorization: Bearer your_token_here" \
-H "Content-Type: application/json" \
-d '{"is_enabled": true}'
Flow at a glance¶
sequenceDiagram
participant Your app
participant PlusPlus API
Your app->>PlusPlus API: POST /automated-rules/{public_id}/run/
PlusPlus API-->>Your app: 201 { public_id, status: "pending" }
loop until status is terminal
Your app->>PlusPlus API: GET /automated-rules/{public_id}/runs/{run_id}/
PlusPlus API-->>Your app: 200 { status, matched_users, affected_users }
end
PlusPlus API--)Your app: webhook automated_rule.run_completed / run_failed
1. Trigger a run¶
curl -X POST https://acme.plusplus.app/api/v2/automated-rules/550e8400-e29b-41d4-a716-446655440000/run/ \
-H "Authorization: Bearer your_token_here"
Execution is asynchronous — the response is 201 Created immediately, with the run queued (status: "pending"):
{
"public_id": "6f1a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8",
"rule": { "public_id": "550e8400-e29b-41d4-a716-446655440000", "name": "Auto-enroll new hires in onboarding" },
"status": "pending",
"started_at": null,
"stopped_at": null,
"matched_users": null,
"affected_users": null,
"created": "2026-07-10T10:30:45.123Z"
}
Hold on to the run's public_id — it's how you poll for the outcome. Triggering a rule that's already running, or that already has a run queued, returns 400 (already_running / already_queued) rather than queuing a second run.
2. Learn the outcome — poll or subscribe¶
Poll¶
curl https://acme.plusplus.app/api/v2/automated-rules/550e8400-e29b-41d4-a716-446655440000/runs/6f1a2b3c-4d5e-6f70-8192-a3b4c5d6e7f8/ \
-H "Authorization: Bearer your_token_here"
status moves from pending → running → a terminal value: ok, warning (no people matched), partially_failed, or failed. matched_users and affected_users populate once the run starts. Poll every few seconds; most rules finish quickly, but a rule enrolling thousands of people can take longer.
Or subscribe to the webhook events¶
Rather than polling, subscribe to automated_rule.run_completed (ok/warning/partially_failed) and automated_rule.run_failed (failed). Both deliver the same shape as the run endpoint above as data.object, and fire for every run — whether you triggered it via POST .../run/ or it ran on the platform's periodic sweep. See the Webhooks section of the API reference for the full event schema and subscription setup.
Rule history¶
GET /automated-rules/{public_id}/runs/ returns a paginated list of a rule's past runs, most recent first — useful for auditing or reconciling after a partially_failed outcome.
Common pitfalls¶
- Treating
201as done. Like the bulk async pattern, a201onrun/means queued, not finished. - Retriggering a running rule.
already_running/already_queued(400) are expected responses if you callrun/again before the previous run finishes — not a transient error to retry-loop on. - Assuming every rule is runnable through this API. Only rules built from operations this API supports (enrollment, content assignment, group sync) are visible here; a rule using an unsupported operation 404s.