Playbooks Overview¶
OpenSOAR playbooks are plain Python modules. The system discovers them from configured directories and registers them through decorators at import time.
Basic Structure¶
from opensoar import playbook, action, resolve_current_alert
import asyncio
@action(name="enrich_ip", timeout=30, retries=2, retry_backoff=2.0)
async def enrich_ip(ip: str | None) -> dict:
if not ip:
return {"status": "missing"}
return {"status": "ok", "ip": ip}
@playbook(
trigger="webhook",
conditions={"severity": ["high", "critical"]},
description="Enrich and triage high-severity alerts",
)
async def triage_high_severity(alert_data):
result = await enrich_ip(alert_data.get("source_ip"))
if result.get("status") == "ok":
await resolve_current_alert(
determination="benign",
reason="Issue remediated automatically by playbook",
)
return {"enrichment": result}
Core Concepts¶
@playbook¶
The @playbook decorator defines:
- trigger type
- matching conditions
- optional description
- whether the playbook is enabled
- optional execution
order
Condition matching follows a simple rule set:
- different condition keys are combined with logical
AND - scalar fields match by exact equality
- list-valued condition fields such as
tagsmatch on overlap
So this playbook:
@playbook(
trigger="webhook",
conditions={
"hostname": "ai-brain",
"tags": ["authentication", "brute-force"],
"severity": ["high", "critical"],
},
)
only matches when all three things are true at once:
- the alert
hostnameis exactlyai-brain - the alert
tagsoverlap with eitherauthenticationorbrute-force - the alert
severityishighorcritical
@action¶
The @action decorator wraps a step with:
- execution tracking
- timeout handling
- retries
- backoff
Parallel Execution¶
Use normal Python async patterns:
vt_result, abuse_result = await asyncio.gather(
lookup_virustotal(iocs),
lookup_abuseipdb(source_ip),
)
Explicit Execution Order¶
When multiple playbooks match the same alert, OpenSOAR executes them in ascending order.
@playbook(
trigger="webhook",
conditions={"tags": ["docker"]},
order=10,
)
async def prepare_docker_recovery(alert_data):
...
@playbook(
trigger="webhook",
conditions={"tags": ["docker"]},
order=20,
)
async def restart_docker_service(alert_data):
...
Lower numbers run first. The default is 1000, so only set order when the sequence matters operationally.
Resolving The Current Alert¶
When a playbook is running for a specific alert, it can resolve that bound alert directly:
from opensoar import resolve_current_alert
await resolve_current_alert(
determination="benign",
reason="Issue remediated automatically by playbook",
)
This is the supported path for automation that wants to mark the current alert as resolved after remediation succeeds.
Updating The Current Alert Without Resolving It¶
If a playbook finishes its own automation but wants a human to continue, use the generic helper:
from opensoar import update_current_alert
await update_current_alert(
status="in_progress",
determination="suspicious",
reason="Playbook completed triage but needs analyst follow-up",
)
Use this when the automation has done useful work but the alert should stay open.
Commenting On The Current Alert¶
If a playbook wants to leave a note for the next human or for auditability:
from opensoar import add_current_alert_comment
await add_current_alert_comment(
"Playbook completed triage and left this for analyst follow-up",
)
Assigning The Current Alert¶
If a playbook wants to hand the current alert to a specific analyst:
Calling assign_current_alert() with no arguments unassigns the current alert.
For the broader status/determination model, read Alert Lifecycle.
Recommended Workflow¶
- Write a playbook in
playbooks/or another configured directory. - Test it like normal Python code.
- Start or restart the API and worker so the playbook is discovered consistently.
- Confirm it appears in the playbooks API or UI.
- Trigger it with a matching alert.
The API and UI expose the persisted execution_order, and the playbooks list is returned in that order.
What OpenSOAR Does Not Have Yet¶
- no dedicated playbook upload API
- no dedicated playbook UI publishing workflow
- no separate sync button for playbooks
For the current loading model, read Loading and Syncing Playbooks.