Getting Started¶
Install¶
pip install pyfsr
Optional extras:
pip install "pyfsr[mcp]" # bundled Model Context Protocol server
pip install "pyfsr[dev]" # linters + test + docs + mcp extras
Connect¶
Create a FortiSOAR client with either an API token or a
(username, password) tuple:
from pyfsr import FortiSOAR
# API token (recommended)
client = FortiSOAR("soar.example.com", "your-api-token")
# Username / password
client = FortiSOAR("soar.example.com", ("admin", "password"))
For self-signed appliances you can disable certificate verification (and silence the resulting warnings):
client = FortiSOAR(
"soar.example.com",
"your-api-token",
verify_ssl=False,
suppress_insecure_warnings=True,
)
See Authentication for environment-based config.
First calls¶
>>> client = demo_client()
>>> # Generic CRUD against any module
>>> incidents = client.records("incidents")
>>> incident = incidents.get("0740411d-e852-4eee-b33b-596210d09a9b")
>>> incident["name"]
'pyfsr doctest incident'
>>> # Raw REST escape hatch
>>> data = client.get("/api/3/alerts")
>>> data["hydra:totalItems"]
1
client.alerts.list()/.get(uuid) work the same way as the generic path above,
via the typed, module-specific AlertsAPI – see
Working with Records for the typed-model walkthrough.
Creating records & picklists¶
Picklist fields (severity, status, …) are stored as IRIs, not friendly
strings – but pyfsr resolves friendly values for you automatically, so you can
just pass "High":
alert = client.alerts.create(
name="Test Alert",
description="This is a test alert",
severity="High", # resolved to its IRI automatically
)
Same on the generic record path – captured live (created, fetched, deleted in the same session; box left with no extra incidents):
>>> created = client.records("incidents").create(
... {"name": "pyfsr doctest incident", "description": "temporary, will be deleted",
... "severity": "Critical"},
... resolve_picklists=False, # already an IRI-resolved doctest fixture; see note below
... )
>>> created["name"]
'pyfsr doctest incident'
Note
Resolution is on by default (the example above passes resolve_picklists=False
only because this doctest replays a captured response rather than a live
metadata lookup). Pass it yourself to skip resolution – and the metadata lookup
it needs – when every value you send is already an IRI.
Triggering playbooks¶
Fire a playbook and wait for it to finish in one call with
run_and_wait():
result = client.playbooks.run_and_wait("My Playbook", timeout=60)
if result.succeeded:
for step in result.steps:
print(f" {step.name:30} {step.status:10} {step.duration_ms}ms")
else:
print(f"failed at: {result.failure.failing_step}")
print(f" error: {result.failure.error_message}")
For playbooks that pause on a Manual Input step, use
trigger() then
answer() to resume. See
Playbook Authoring & Deployment for the full interactive flow.
Next steps¶
Working with Records – generic CRUD and typed models
Querying – the fluent
QueryDSLPlaybook Authoring & Deployment – author, deploy, and trigger playbooks from YAML
Playbook YAML Syntax Reference – the full step-type syntax reference
AI & Agents – drive FortiSOAR from an LLM