Typed models¶
Every typed model re-exported from pyfsr.models – the shapes returned by
the client APIs and the validated argument bundles accepted by the write
verbs.
Note
This page is generated from pyfsr.models.__all__. The classes themselves
live in private submodules (_playbooks.py, _generated.py, …), which
autoapi does not page – so documenting them here under their public
name is what gives pyfsr.models.X a resolvable target. Without it every
{class}~pyfsr.models.X`` cross-reference in the docs is silently dead.
Records (module entities)¶
- class pyfsr.models.Alert(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, sourceId: str | None = None, source: str | None = None, description: str | None = None, type: PicklistIRI | None = None, severity: PicklistIRI | None = None, status: PicklistIRI | None = None, assignedTo: RecordIRI | User | None = None, dueDate: int | None = None, createUser: str | dict[str, Any] | None = None, modifyUser: RecordIRI | dict[str, Any] | None = None, createDate: float | None = None, modifyDate: float | None = None, id: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordAn Alert record. Field set is illustrative - 127 properties exist on this entity per the Hydra walk; the ones below are the most-used. Full list via GET /api/3/contexts/Alert.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- severity: PicklistIRI | None¶
- status: PicklistIRI | None¶
- type: PicklistIRI | None¶
- class pyfsr.models.Comment(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, attachments: list[Any] | None = None, approvals: list[Any] | None = None, content: str | None = None, tasks: list[Any] | None = None, people: list[Any] | None = None, type: PicklistIRI | None = None, alerts: list[Any] | None = None, isDeleted: str | None = None, assets: list[Any] | None = None, file: str | None = None, file1: str | None = None, campaigns: list[Any] | None = None, file2: str | None = None, rawCommentData: str | None = None, communication: list[Any] | None = None, file3: str | None = None, events: list[Any] | None = None, file4: str | None = None, incidents: list[Any] | None = None, isImportant: bool | None = None, indicators: list[Any] | None = None, peopleUpdated: bool | None = None, replyTo: Any | None = None, warrooms: list[Any] | None = None, devices: list[Any] | None = None, replies: list[Any] | None = None, lastReplyDate: float | None = None, managers: list[Any] | None = None, scenario: list[Any] | None = None, cVEs: list[Any] | None = None, scans: list[Any] | None = None, vulnerabilities: list[Any] | None = None, hunt: list[Any] | None = None, threatActors: list[Any] | None = None, threatIntelReports: list[Any] | None = None, workspaces: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA Comment record. Field set derived from GET /api/3/model_metadatas?$relationships=true. Flags: ownable, taggable.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- type: PicklistIRI | None¶
- class pyfsr.models.Incident(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, responseSLAResumeDate: float | None = None, mitretechniques: list[Any] | None = None, mitresubtechniques: list[Any] | None = None, mitremitigations: list[Any] | None = None, mitregroups: list[Any] | None = None, mitretactics: list[Any] | None = None, mitresoftware: list[Any] | None = None, state: PicklistIRI | None = None, escalated: str | None = None, ticketID: str | None = None, impactROI: int | None = None, wasPersonalDataAffected: PicklistIRI | None = None, warrooms: list[Any] | None = None, incRemainingRespSLA: int | None = None, incRemainingAckSLA: int | None = None, respSLApausedon: float | None = None, ackSLApausedon: float | None = None, volatileData: str | None = None, businessImpact: str | None = None, comments: list[Any] | None = None, companies: list[Any] | None = None, confirmationDate: float | None = None, senderEmailAddress: str | None = None, eradicationDate: float | None = None, filehash: str | None = None, identificationDate: float | None = None, impactAssessments: str | None = None, incidentLead: RecordIRI | User | None = None, incidentsummary: str | None = None, indicators: list[Any] | None = None, metrics: str | None = None, nextsteps: str | None = None, persons: list[Any] | None = None, phase: PicklistIRI | None = None, incidentphase: str | None = None, recoveryDate: float | None = None, resDate: float | None = None, resDueBy: float | None = None, receipientEmailAddress: str | None = None, recoveryTime: int | None = None, resolution: str | None = None, resolveddate: float | None = None, resSla: PicklistIRI | None = None, resPercentSla: int | None = None, senderDomain: str | None = None, severity: PicklistIRI | None = None, sourceId: str | None = None, targetAsset: str | None = None, tasks: list[Any] | None = None, category: PicklistIRI | None = None, ackDueDate: float | None = None, responseDate: float | None = None, otherLogs: str | None = None, siemQuery: str | None = None, fileName: str | None = None, name: str | None = None, alerts: list[Any] | None = None, assets: list[Any] | None = None, campaigns: list[Any] | None = None, communications: list[Any] | None = None, mitreattackid: str | None = None, c2server: str | None = None, dLLName: str | None = None, processName: str | None = None, affectedUser: str | None = None, affectedHost: str | None = None, pcapFile: str | None = None, ackDate: float | None = None, slaState: PicklistIRI | None = None, slaPercentage: int | None = None, aftermathDate: float | None = None, assigneddate: float | None = None, attachments: list[Any] | None = None, containmentDate: float | None = None, containmentTime: int | None = None, dateOfIncident: float | None = None, deliveryVector: PicklistIRI | None = None, description: str | None = None, destinationIP: str | None = None, deviceUID: str | None = None, discoveredOn: float | None = None, dwellTime: int | None = None, source: str | None = None, sourcedata: str | None = None, sourceIP: str | None = None, cVEs: list[Any] | None = None, status: PicklistIRI | None = None, vulnerabilities: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordAn Incident record. Field set derived from GET /api/3/model_metadatas?$relationships=true. Unique constraints: [{‘incidents_unique’: {‘columns’: [‘sourceId’, ‘tenant’]}}]. Flags: taggable, queueable.
- category: PicklistIRI | None¶
- deliveryVector: PicklistIRI | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- phase: PicklistIRI | None¶
- resSla: PicklistIRI | None¶
- severity: PicklistIRI | None¶
- slaState: PicklistIRI | None¶
- state: PicklistIRI | None¶
- status: PicklistIRI | None¶
- wasPersonalDataAffected: PicklistIRI | None¶
- class pyfsr.models.Task(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, submittedBy: RecordIRI | User | None = None, name: str | None = None, description: str | None = None, type: PicklistIRI | None = None, dueBy: float | None = None, assignedOnDate: float | None = None, startDate: float | None = None, completedOnDate: float | None = None, actualMinutes: int | None = None, priority: PicklistIRI | None = None, status: PicklistIRI | None = None, assignedToPerson: RecordIRI | User | None = None, companies: list[Any] | None = None, persons: list[Any] | None = None, alerts: list[Any] | None = None, attachments: list[Any] | None = None, assets: list[Any] | None = None, comments: list[Any] | None = None, incidents: list[Any] | None = None, indicators: list[Any] | None = None, warrooms: list[Any] | None = None, approvalhost: str | None = None, cVEs: list[Any] | None = None, vulnerabilities: list[Any] | None = None, workflowid: str | None = None, taskdata: str | None = None, tasktype: str | None = None, hunt: list[Any] | None = None, stepid: int | None = None, threatIntelFeeds: list[Any] | None = None, workspaces: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA Task record. Field set derived from GET /api/3/model_metadatas?$relationships=true. Flags: ownable, taggable, queueable.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- priority: PicklistIRI | None¶
- status: PicklistIRI | None¶
- type: PicklistIRI | None¶
Integrations & connectors¶
- class pyfsr.models.ApiResult(**extra_data: Any)[source]¶
Bases:
BaseModelDict-compatible base for typed API result shapes.
Subclasses get attribute access (
r.config_id) and dict-style subscripting (r["config_id"]), so callers don’t need to migrate all at once. Unknown fields from the wire are preserved underextra.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ConfigValidationError(*, field: str | None = None, code: str | None = None, message: str | None = None, valid_options: list[Any] | None = None, expected: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA single field-level error from
validate_config().- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ConfigValidationResult(*, valid: bool = False, missing: list[str] = <factory>, invalid: list[str] = <factory>, unknown: list[str] = <factory>, errors: list[ConfigValidationError] = <factory>, **extra_data: Any)[source]¶
Bases:
ApiResultReturn value of
client.connectors.validate_config().validisTrueonly whenmissingandinvalidare both empty.unknownfields are reported but do not make the config invalid.- errors: list[ConfigValidationError]¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ConnectorConfig(*, id: int | None = None, config_id: str | None = None, name: str | None = None, default: bool = False, status: int | None = None, config: dict[str, ~typing.Any]=<factory>, connector: int | None = None, agent: str | None = None, teams: list[Any] = <factory>, remote_status: dict[str, ~typing.Any]=<factory>, health_status: dict[str, ~typing.Any]=<factory>, **extra_data: Any)[source]¶
Bases:
ApiResultA connector configuration record from
/api/integration/configuration/.Returned by
create_configuration(),update_configuration(), andlist_configurations().configis the live field map – its shape varies by connector.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ConnectorConfigSummary(*, id: int | None = None, config_id: str | None = None, name: str | None = None, default: bool = False, **extra_data: Any)[source]¶
Bases:
ApiResultA single configuration entry embedded in the connector listing.
From
/api/integration/connectors/configuration[].- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ConnectorDefinition(*, name: str | None = None, version: str | None = None, label: str | None = None, description: str | None = None, publisher: str | None = None, category: str | list[str] | None = None, active: bool | None = None, cs_approved: bool | None = None, cs_compatible: bool | None = None, operations: list[Operation] = <factory>, config_schema: ConfigSchema = <factory>, configuration: Any = None, **extra_data: Any)[source]¶
Bases:
ApiResultA connector’s full definition (config schema + operations).
Returned by
definition()– thePOST /api/integration/connectors/<name>/<version>/?format=jsonpayloadwarm_catalogreads to sync the installed connector catalog.categorymay arrive as a string or a list; both are tolerated. Curated fields are typed;config_schema/configurationstay loose (shape varies by connector). Dict-compatible.- config_schema: ConfigSchema¶
- configuration: Any¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.DependencyStatus(*, dependencies_installed: bool | None = None, message: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultPython-dependency state for an installed connector.
From
GET /api/integration/connectors/dependencies_check/<name>/<version>/. The UI mapsdependencies_installedonto the tri-state badgeCompleted/Failed/In-Progressshown on the connector card.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.EnsureVersionResult(*, action: str | None = None, from_version: str | None = None, to: str | None = None, backup: str | None = None, configs_before: int = 0, configs_after: int = 0, **extra_data: Any)[source]¶
Bases:
ApiResultReturn value of
client.connectors.ensure_version().actionis one of"noop","in_place","restored","reinstalled", or"failed".- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ExecuteResult(*, operation: str | None = None, status: str | None = None, message: str | None = None, data: Any = None, **extra_data: Any)[source]¶
Bases:
ApiResultReturn value of
client.connectors.execute().datais the connector’s own output – its shape varies by connector and operation.- data: Any¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ExportJobResult(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, id: int | None = None, status: str | None = None, errorMessage: str | None = None, fileName: str | None = None, progressPercent: int | None = None, currentlyExporting: str | None = None, type: str | None = None, file: RecordIRI | dict[str, Any] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA
/api/3/export_jobsrecord.Returned by export polling in
export_config.status == "Export Complete"means the archive is ready for download.fileis the/api/3/files/<uuid>record (or its IRI string) once the export finishes.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.HealthcheckResult(*, status: str | None = None, message: str | None = None, name: str | None = None, version: str | None = None, config_id: str | None = None, request_id: str | None = None, http_status: int | None = None, _status: bool | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultReturn value of
client.connectors.healthcheck().status == "Available"is green.status == "no-config"means the connector isn’t configured on this instance (pyfsr-synthesised, not from the wire).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ImportJobResult(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, id: int | None = None, status: str | None = None, errorMessage: str | None = None, logMessages: list[LogMessage] = <factory>, options: dict[str, ~typing.Any] | list=<factory>, file: RecordIRI | dict[str, ~typing.Any] | None=None, jobUuid: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA
/api/3/import_jobsrecord.Returned by
import_config.import_file()and the lower-level job polling methods.status == "Import Complete"means success.optionsis the server-generated import option tree (section → include flags).- logMessages: list[LogMessage]¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.IngestionMetadata(*, id: int | None = None, name: str | None = None, description: str | None = None, configuration: str | None = None, connector: dict[str, ~typing.Any]=<factory>, metadata: dict[str, ~typing.Any]=<factory>, sample_data: Any = None, owners: list[Any] = <factory>, created_by: str | None = None, modified_by: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA
/api/integration/data-import/record.The join between a connector configuration and the periodic task that drives its ingestion. The UI writes one of these per configuration and later re-finds the schedule through
metadata.scheduleId– without it the Configure Data Ingestion screen cannot show an existing schedule.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- sample_data: Any¶
- class pyfsr.models.IngestionPlaybooks(*, fetch: Workflow | None = None, ingest: Workflow | None = None, create: Workflow | None = None, update: Workflow | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe tag-bucketed ingestion playbooks for one connector.
FortiSOAR identifies ingestion playbooks purely by record tag: a playbook tagged
fetchis the sample-data fetcher,ingestis the one the schedule fires,create/updateare the record writers. A single playbook can carry several of these tags at once (FortiSIEM’s FortiSIEM > Ingest is taggedingestandcreate).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.IngestionSetupResult(*, connector: str | None = None, version: str | None = None, config_id: str | None = None, config_name: str | None = None, collection_uuid: str | None = None, collection_name: str | None = None, playbooks: list[Workflow] = <factory>, ingest_playbook_iri: str | None = None, schedule_id: str | None = None, schedule_name: str | None = None, scheduled: bool = False, health_status: str | None = None, cloned: bool = False, dry_run: bool = False, existed: bool = False, **extra_data: Any)[source]¶
Bases:
ApiResultWhat
data_ingest_wizard()built.Mirrors the end state of the UI wizard: a per-configuration playbook collection, the cloned+rewritten ingestion playbooks inside it, the periodic task that fires the
ingestplaybook, and thedata-importmetadata record that ties them together.- existed: bool¶
Truewhenensure_ingestion()found ingestion already configured and returned it without writing (“get”);Falsewhen the wizard actually built it (“make”).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.IngestionStatus(*, connector: str | None = None, config_id: str | None = None, collection_exists: bool = False, playbooks: IngestionPlaybooks = <factory>, metadata: IngestionMetadata | None = None, schedule_id: str | None = None, schedule_name: str | None = None, schedule_enabled: bool | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe current data-ingestion state of one connector configuration.
The read-only counterpart to
data_ingest_wizard(): it inspects what the wizard would have built – the per-configuration collection, the cloned ingestion playbooks, the periodic task, and thedata-importmetadata record – and reports whether each piece is present, without writing anything.- property configured: bool¶
Whether ingestion has been set up at all for this configuration.
Trueonce the collection exists and holds aningest-tagged playbook – the minimum the Trigger Ingestion Now button needs. A configured setup may still be unscheduled (schedule_id is None).
- metadata: IngestionMetadata | None¶
data-importmetadata record for this config, if one was written.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- playbooks: IngestionPlaybooks¶
The ingestion playbooks found in that collection, bucketed by role.
- class pyfsr.models.IngestionTeardownResult(*, connector: str | None = None, config_id: str | None = None, schedule_name: str | None = None, schedule_deleted: bool = False, metadata_ids: list[int] = <factory>, metadata_deleted: int = 0, collection_uuid: str | None = None, collection_deleted: bool = False, dry_run: bool = False, **extra_data: Any)[source]¶
Bases:
ApiResultWhat
remove_ingestion()removed.The inverse of
IngestionSetupResult: it records which of the wizard’s four artifacts were torn down – the periodic task, thedata-importmetadata record(s), and the per-configuration collection (which cascades the cloned playbooks). Withdry_run=Truethe*_deletedflags/counts stayFalse/0and the fields report what would be removed.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.InstallJobStatus(*, status: str | None = None, progressPercent: int | None = None, errorMessage: str | None = None, currentlyImporting: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultProgress record for a connector install import job.
Returned by
install_status()andwait_for_install().status == "Import Complete"means the install finished successfully.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.InstalledConnector(*, id: int | None = None, name: str | None = None, version: str | None = None, label: str | None = None, active: bool | None = None, system: bool | None = None, config_count: int | None = None, status: str | None = None, configuration: list[ConnectorConfigSummary] = <factory>, ingestion_supported: bool | None = None, tags: list[Any] = <factory>, agent: str | None = None, development: bool | None = None, created: str | None = None, modified: str | None = None, publisher: str | None = None, contributor: str | None = None, rpm_installed: bool | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultAn installed connector entry from
GET /api/integration/connectors/.Only the fields that are stable and useful for code are typed; the rest (icons, descriptions, help links) live in
extra.- configurations: list[ConnectorConfigSummary]¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.IntegrationListEnvelope(*, status: str | None = None, totalItems: int | None = None, itemsPerPage: int | None = None, nextPage: int | None = None, previousPage: int | None = None, data: list[Any] = <factory>, **extra_data: Any)[source]¶
Bases:
ApiResultThe custom (non-Hydra) list envelope several
/api/integrationendpoints return.Unlike the JSON-LD collection wrapped by
HydraPage(hydra:member/hydra:totalItems), endpoints likeGET /api/integration/connectors/andGET /api/integration/configuration/page with a plain envelope:{"status": "...", "totalItems": 73, "itemsPerPage": 30, "nextPage": 2, "previousPage": null, "data": [ {...}, ... ]}
Typed once here so callers parse it the same way everywhere (it has been mis-read as a bare list more than once).
datastayslist[Any]– the per-endpoint method validates each row into its own model.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- classmethod parse(response: Any) IntegrationListEnvelope[source]¶
Coerce a raw response into an envelope, tolerating a bare list/None.
A dict is validated as the envelope; a bare list is wrapped as its
data(some endpoints/versions return the array directly); anything else yields an empty envelope.
- class pyfsr.models.LogMessage(*, message: str | None = None, date: int | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA single entry from an import job’s
logMessageslist.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.Operation(*, operation: str | None = None, title: str | None = None, description: str | None = None, annotation: str | None = None, category: str | None = None, visible: bool = True, enabled: bool = True, parameters: list[OperationParam] = <factory>, output_schema: Any = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne action a connector exposes, from its definition’s
operations[].Richer than
ConnectorOperation(the Content-Hub catalog shape) – this is the runtime definition, carrying typedOperationParaminputs.visible/enableddefault toTruewhen omitted. Dict-compatible (op["operation"]still works).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- output_schema: Any¶
- parameters: list[OperationParam]¶
- ui_params(*, required_only: bool = False, selections: dict[str, Any] | None = None) list[OperationParam][source]¶
The params a UI/agent must render to stage this action, ordered.
Returns the operation’s
parametersfiltered to the visible ones (hidden params are platform-managed, not author-facing), ordered required-first then by declared order, and deduped by name – a param that appears in several conditionalonchangegroups (e.g.block_ip’sipunder eachmethodoption) collapses to its first occurrence, so the list reads as one form, not a repeated field.This is the schema every action-staging caller was re-deriving by hand (the fsr-playbook-framework MCP discovery tool’s
_param_sig/_required_params). Each returnedOperationParamstill carries itstype/title/requiredand – for aselect– itsselect_options(), so the caller picks valid param names and valid choice values straight from the definition.Conditional reveal (``selections``). A
selectparam can gate further inputs via itsonchangemap (option value → the sub-params that become active when it is chosen). By default those sub-params are not returned – the base form only. Passselections(a{param_name: chosen_value}map of what the user has picked so far) to also include the sub-params those choices reveal, so you render only the fields actually needed for the current state. On box 206, choosingsmtp/send_email_new’styperevealsto/cc/bcc:op.ui_params() # base params only op.ui_params(selections={"type": "Team"}) # base + to, cc, bcc
Reveal is recursive (a revealed sub-param may itself be a gating
select) and matches a selection against theonchangekeys by the value or its string form. Revealed params fold into the same visible/required-first/deduped result. Unrecognized selections (unknown param, or a value with no matching branch) are ignored.Pass
required_only=Trueto keep just the required params.
- class pyfsr.models.OperationParam(*, name: str | None = None, title: str | None = None, type: str | None = None, description: str | None = None, tooltip: str | None = None, placeholder: str | None = None, required: bool = False, value: Any = None, visible: bool = True, editable: bool = True, onchange: dict[str, list[~pyfsr.models._integration.OperationParam]]=<factory>, options: list[Any] = <factory>, apiOperation: str | None = None, apiOnchange: bool = False, **extra_data: Any)[source]¶
Bases:
ApiResultOne input parameter of a connector operation, from a connector definition.
The
parameters[]of an operation inPOST /api/integration/connectors/<name>/<version>/?format=json.valueis the declared default (its type varies by field).visible/editabledefault toTruewhen the wire omits them.onchangeis typed recursively (dict[str, list[OperationParam]]) so conditional sub-params validate too;optionsstayslist[Any](plain strings or{value,title}dicts).Dynamic dropdowns:
apiOperationnames a sibling connector operation whose result populates the choices at render time (e.g.get_incident_severities);apiOnchange=Truere-calls it whenever a sibling field changes (cascading dropdowns). See the Connector Building Guide section “Dynamic Options from an Operation”.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- onchange: dict[str, list[OperationParam]]¶
- select_options() list[ParamOption][source]¶
The param’s choices as normalized
ParamOptions.Empty for a non-
selectparam. Tolerates both wire shapes: a bare string"Basic"yieldsParamOption(value="Basic", title="Basic"); a{"value","title"}dict maps across, defaulting a missingtitletostr(value)(and a missingvaluetotitle) so a caller always has both a send-value and a label.
- value: Any¶
- class pyfsr.models.ParamOption(*, value: Any = None, title: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne choice of a
select/multiselectconnector-operation parameter.Normalized from the two wire shapes an operation’s
options[]uses (seeOperationParam.select_options()): a plain string"Basic"becomesParamOption(value="Basic", title="Basic"); a{"value": ..., "title": ...}dict maps straight across (either key may be absent – the other fills in).valueis what you send as the param;titleis the label a UI shows.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- value: Any¶
Playbooks & runs¶
- class pyfsr.models.ApprovalRequest(*, decision: str, comment: str | None = None)[source]¶
Bases:
_RequestModelTyped body for
approval().- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.CreatePlaybookRequest(*, name: str, collection: str, is_active: bool = True, remote_executable: bool = False, priority: str | None = None, origin: str | None = None, **extra_data: Any)[source]¶
Bases:
_RequestModelTyped body for
create_playbook().Deliberately shallow: it validates the playbook-definition envelope (name / collection / flags / picklist IRIs) and passes any other fields through verbatim. The deep step/route shape is owned by the
fsr_playbookscompiler, not this model.- model_config = {'extra': 'allow'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.CreateVersionRequest(*, workflow: str, json: str, note: str = '', modify_date: int | None = None)[source]¶
Bases:
_RequestModelTyped body for
create_version().Mirrors FortiSOAR’s editor
saveSnapshotwire:jsonis the prepared workflow stringified,workflowis the workflow IRI,modifyDateis an epoch second timestamp.notelabels the snapshot.- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.PlaybookVersion(*, note: str | None = None, autosave: bool | None = None, uuid: str | None = None, json: str | None = None, workflow: Any | None = None, createDate: float | None = None, modifyDate: float | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne saved playbook snapshot (the
workflow_versionsmodule).FortiSOAR’s playbook “version control” is a snapshot history, not a revision/diff resource: each version is a frozen copy of the playbook stored under
/api/3/workflow_versions(capped at 20 per playbook). A version is either a manual snapshot (autosave=False, a caller-suppliednote) or an editor auto-save (autosave=True).jsonis the snapshot payload – the full workflow definition stringified (steps/routes/groups/triggerStep/ …). It is populated onlist_versions/get_versionbut not echoed back bycreate_version(the server omits the blob on the POST response); fetch the version again to readjson.workflowis the embedded workflow the snapshot belongs to.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ResumeRequest(*, manual_input_id: int, input: Any = None, step_iri: str | None = None, step_id: str | None = None, approved: bool | None = None)[source]¶
Bases:
_RequestModelTyped body for
resume()(manual-input / approval resume).- input: Any¶
- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RunEnv(*, name: str | None = None, env: dict[str, ~typing.Any]=<factory>, status: str | None = None, steps: dict[str, ~pyfsr.models._playbooks.RunStep]=<factory>, **extra_data: Any)[source]¶
Bases:
ApiResultA run’s Jinja-context view, from
run_env().envis the run’s top-level environment (input/request/resources/…);stepsis keyed by step display name. In Jinja a step is referenced asvars.steps.<name with spaces replaced by underscores>.nameis the run’s playbook display name (handy for pulling the live playbook back).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RunFailure(*, status: str | None = None, failing_step: str | None = None, error_message: str | None = None, pk: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe slim failure projection from
why_failed().failing_stepis the display name of the first non-success step (Noneif the run succeeded);error_messageis the step-level error when present, else the run’s top-level error.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RunNode(*, pk: str | None = None, name: str | None = None, status: str | None = None, task_id: str | None = None, children: list[RunNode] = <factory>, steps: list[RunStepSnapshot] = <factory>, **extra_data: Any)[source]¶
Bases:
ApiResultOne node in a run tree, from
run_tree().The run plus its referenced-child runs (linked by
parent_wf), recursively.pkis the numeric run id;childrenare the sub-playbook runs this run spawned. Encodes the trigger->run->child linkage so callers don’t have to find the parent by name in the raw/api/wf/api/workflowslisting.stepscarries a slim per-step snapshot (name/status/result_preview) on the root node whenrun_tree(steps=True); empty otherwise (and always empty on child nodes). Callrun_env()for a child’s full step detail.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- steps: list[RunStepSnapshot]¶
- class pyfsr.models.RunResult(*, status: str | None = None, task_id: str | None = None, pk: str | None = None, name: str | None = None, steps: list[RunStepSnapshot] = <factory>, failure: RunFailure | None = None, children: list[RunResult] = <factory>, **extra_data: Any)[source]¶
Bases:
ApiResultThe typed result of
run_and_wait().The all-in-one outcome of a trigger-and-poll cycle: the run’s terminal status, per-step snapshots (with timing), failure details, and child-run results (for sub-playbook chains). Everything an agent needs to debug a playbook in one object.
- failure: RunFailure | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- property slow_steps: list[RunStepSnapshot]¶
Steps flagged slow by each step’s own
RunStepSnapshot.slow_threshold_ms(default 30s).
- steps: list[RunStepSnapshot]¶
- class pyfsr.models.RunStep(*, status: str | None = None, result: Any | None = None, start_time: str | None = None, end_time: str | None = None, duration_ms: int | None = None, slow_threshold_ms: int = 30000, **extra_data: Any)[source]¶
Bases:
ApiResultOne step’s outcome within a run, as reshaped by
run_env().Timing fields (
start_time/end_time/duration_ms) are parsed from the wire’sstarted/completedISO timestamps – available whenstep_detail=True(whichrun_env()always sets).- property is_slow: bool¶
Truewhenduration_msexceedsslow_threshold_ms(default 30s).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RunStepSnapshot(*, name: str | None = None, status: str | None = None, result_preview: str | None = None, start_time: str | None = None, end_time: str | None = None, duration_ms: int | None = None, slow_threshold_ms: int = 30000, **extra_data: Any)[source]¶
Bases:
ApiResultA slim per-step outcome snapshot for
run_tree()(steps=True).A trimmed preview of a step’s result – enough for an agent to decide whether to call
run_env()for the full detail, without the full result bloating the tree.result_previewis the step’sresultJSON-encoded and capped to ~500 chars.Timing fields (
start_time/end_time/duration_ms) are parsed from the wire’sstarted/completedISO timestamps.- property is_slow: bool¶
Truewhenduration_msexceedsslow_threshold_ms(default 30s).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RunSummary(*, task_id: str | None = None, name: str | None = None, status: str | None = None, error_message: str | None = None, modified: str | None = None, uuid: str | None = None, pk: str | None = None, source: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA flattened playbook-run summary (the default
PlaybooksAPIview).Produced by
_shape_runforexecution_history(),last_run(),wait(), and friends.pkis the trailing segment of the run’s@id(whatget_execution()takes);sourceis"live"or"historical"(which run table it came from).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.TriggerActionRequest(*, module: str, record_uuid: str, playbook_uuid: str | None = None, env: dict[str, ~typing.Any]=<factory>)[source]¶
Bases:
_RequestModelTyped body for
trigger_action()(the record-action /cybersponse.actiontrigger route).- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.TriggerRequest(*, records: list[str] = <factory>, inputs: dict[str, ~typing.Any] | None=None, env: dict[str, ~typing.Any]=<factory>)[source]¶
Bases:
_RequestModelTyped body for
trigger().recordsaccepts a single ref or a list; bare uuids/refs are expanded to/api/3/alerts/<uuid>IRIs.envkeys are merged into the body verbatim for the rare playbook expecting a custom trigger envelope.- model_config = {'extra': 'forbid'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.TriggerResponse(*, task_id: str | list[str] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe response from a trigger verb (
trigger/trigger_by_name/trigger_action).Normally
{"task_id": "<run-uuid>"}, but a trigger that starts more than one run (e.g. an API-endpoint route bound to several playbooks) returnstask_idas a list of run-uuids – so this accepts either. Extra keys (e.g. a deferred 202 envelope) are preserved. Usetask_idsfor a uniform list, ortask_idto track the started run withwait().The routes do not agree on the key. Live-verified on the record-action route (
/api/triggers/1/action/<route>): it answers{"task_ids": [...]}– plural – where the manual-execute route (notrigger) answers{"task_id": "..."}. Because onlytask_idwas declared, a wiretask_idsused to land inmodel_extrawhile thetask_idsproperty (which normalizestask_id) shadowed it and returned[]– sotrigger_actioncallers could not reach the run they had just started through either accessor._absorb_plural_task_idsfolds the plural wire key intotask_idbefore validation, making both accessors work for both routes and honouring this docstring’s “task_id may be a list” contract.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.VersionDiff(*, added: list[str] = <factory>, removed: list[str] = <factory>, changed: list[VersionStepDelta] = <factory>, routes_added: list[str] = <factory>, routes_removed: list[str] = <factory>, groups_added: list[str] = <factory>, groups_removed: list[str] = <factory>, **extra_data: Any)[source]¶
Bases:
ApiResultA step-graph diff between two playbook snapshots (
diff_versions()).Steps are keyed by
uuid.added/removedare step uuids present in only one side;changedholds per-step field deltas.routes/groupsare the simpler added/removed-uuid lists for those graphs.- changed: list[VersionStepDelta]¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.VersionStepDelta(*, step: str | None = None, field: str | None = None, from_value: Any | None = None, to: Any | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne changed step between two playbook versions (
diff_versions()).fieldis the top-level step key that differs (arguments,name,stepType…);from/toare the old / new values (Any– may be dicts, strings, orNone).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.WaitProgress(*, tree: RunNode, poll_count: int, elapsed_s: float, is_terminal: bool)[source]¶
Bases:
BaseModelA single poll snapshot handed to the
on_pollcallback ofwait_for_task().Lets a caller act on the live run between polls – answer a pending manual-input gate, patch a field to unblock an SLA timer, log progress – without re-implementing the poll loop.
treeis the freshly-fetchedRunNode;poll_countcounts polls so far (1-based);elapsed_sis seconds since the wait began;is_terminalisTrueon the final poll (the run has reached a terminal status and the wait is about to return).Return
Falsefrom the callback to stop waiting early and return the current tree; returnNone/True(or nothing) to keep polling.- model_config = {'arbitrary_types_allowed': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Module administration¶
- class pyfsr.models.AttributeBulkAction(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, allow: bool = False, buttonText: str = '', buttonIcon: str = '', buttonClass: str = '', **extra_data: Any)[source]¶
Bases:
BaseRecordbulkActionsub-object on an attribute.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AttributeMetadata(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, type: str | None = None, formType: str | None = None, length: int | None = None, orderIndex: int | None = None, collection: bool = False, system: bool = False, encrypted: bool = False, searchable: bool = False, peerReplicable: bool = True, gridColumn: bool = False, skipSerialization: bool = False, htmlEscape: bool = False, visibility: bool | dict[str, ~typing.Any]=True, readable: bool = True, writeable: bool = True, unique: bool = False, recommend: bool = False, identifier: bool | None = None, orphanRemoval: bool | None = None, ownsRelationship: bool | None = None, inversedField: str | None = None, dataSource: dict[str, ~typing.Any] | list | None=None, dataSourceFilters: dict[str, ~typing.Any] | list | None=None, validation: AttributeValidation | dict[str, ~typing.Any] | None=None, bulkAction: AttributeBulkAction | dict[str, ~typing.Any] | None=None, defaultValue: Any = None, tooltip: str | None = None, displayName: str | None = None, descriptions: dict[str, str] | None=None, importedBy: list[Any] = <factory>, sattrib: RecordIRI | dict[str, ~typing.Any] | None=None, **extra_data: Any)[source]¶
Bases:
BaseRecordA single field definition on a staging or published module.
typeis the Postgres storage type ("string","integer","boolean","object","picklists", or a module name like"alerts"for relationships).form_typeis the display type (the field kind shown in the editor).The
sattribfield is the IRI of the parentStagingModelMetadata(or its dict form when the attribute is fetched via the staging module endpoint).- bulk_action: AttributeBulkAction | dict[str, Any] | None¶
- default_value: Any¶
- property is_relationship: bool¶
True when the field is a relationship (lookup / manyToMany / oneToMany).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- validation: AttributeValidation | dict[str, Any] | None¶
- class pyfsr.models.AttributeValidation(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, required: bool = False, minlength: int | None = None, maxlength: int | None = None, _enableRange: bool | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordvalidationsub-object on an attribute.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.DefaultSortEntry(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, field: str | None = None, direction: str | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordOne entry in
defaultSorton a module metadata record.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.InvalidDraft(*, module: str | None = None, uuid: str | None = None, field: str | None = None, problem: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne staged module/field whose name would break the next publish.
Synthesized by
find_invalid_drafts().fieldis set only for an attribute-level problem. Dict-compatible.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ModuleDescriptions(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, singular: str | None = None, plural: str | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecorddescriptionssub-object on a module metadata record.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ModuleMetadata(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, type: str | None = None, module: str | None = None, tableName: str | None = None, parentType: str | None = None, ownable: bool = False, userOwnable: bool = False, queueable: bool = False, trackable: bool = False, taggable: bool = False, peerReplicable: bool = True, indexable: bool = True, writable: bool = True, system: bool = False, softDeleteable: bool = False, archivable: bool = False, paused: bool = False, enableReplication: bool = True, partitionBy: str | None = None, archivalCriteria: dict[str, ~typing.Any] | None=None, archivalFilters: list[Any] = <factory>, replicationFilters: list[Any] = <factory>, defaultSort: list[~pyfsr.models._modules_admin.DefaultSortEntry | dict[str, ~typing.Any]]=<factory>, uniqueConstraint: list[dict[str, ~typing.Any]]=<factory>, displayName: str | None = None, descriptions: ModuleDescriptions | dict[str, str] | None=None, attributes: list[~pyfsr.models._modules_admin.AttributeMetadata | dict[str, ~typing.Any]]=<factory>, importedBy: list[Any] = <factory>, **extra_data: Any)[source]¶
Bases:
BaseRecordShared shape for both staging (
StagingModelMetadata) and published (ModelMetadata) module records.attributesis only populated when the record is fetched individually (GET /api/3/staging_model_metadatas/{uuid}), not in list responses.- attributes: list[AttributeMetadata | dict[str, Any]]¶
- default_sort: list[DefaultSortEntry | dict[str, Any]]¶
- get_attribute(name: str) AttributeMetadata | dict[str, Any] | None[source]¶
Return the attribute with
name, orNone.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.PendingChange(*, module: str | None = None, change: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne module with an uncommitted (staged-but-unpublished) schema change.
Synthesized by
pending_changes()by diffing the staging vs published metadata stores. Dict-compatible (row["module"]).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.PublishedModelMetadata(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, type: str | None = None, module: str | None = None, tableName: str | None = None, parentType: str | None = None, ownable: bool = False, userOwnable: bool = False, queueable: bool = False, trackable: bool = False, taggable: bool = False, peerReplicable: bool = True, indexable: bool = True, writable: bool = True, system: bool = False, softDeleteable: bool = False, archivable: bool = False, paused: bool = False, enableReplication: bool = True, partitionBy: str | None = None, archivalCriteria: dict[str, ~typing.Any] | None=None, archivalFilters: list[Any] = <factory>, replicationFilters: list[Any] = <factory>, defaultSort: list[~pyfsr.models._modules_admin.DefaultSortEntry | dict[str, ~typing.Any]]=<factory>, uniqueConstraint: list[dict[str, ~typing.Any]]=<factory>, displayName: str | None = None, descriptions: ModuleDescriptions | dict[str, str] | None=None, attributes: list[~pyfsr.models._modules_admin.AttributeMetadata | dict[str, ~typing.Any]]=<factory>, importedBy: list[Any] = <factory>, **extra_data: Any)[source]¶
Bases:
ModuleMetadataA published module record from
/api/3/model_metadatas.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StagingModelMetadata(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, type: str | None = None, module: str | None = None, tableName: str | None = None, parentType: str | None = None, ownable: bool = False, userOwnable: bool = False, queueable: bool = False, trackable: bool = False, taggable: bool = False, peerReplicable: bool = True, indexable: bool = True, writable: bool = True, system: bool = False, softDeleteable: bool = False, archivable: bool = False, paused: bool = False, enableReplication: bool = True, partitionBy: str | None = None, archivalCriteria: dict[str, ~typing.Any] | None=None, archivalFilters: list[Any] = <factory>, replicationFilters: list[Any] = <factory>, defaultSort: list[~pyfsr.models._modules_admin.DefaultSortEntry | dict[str, ~typing.Any]]=<factory>, uniqueConstraint: list[dict[str, ~typing.Any]]=<factory>, displayName: str | None = None, descriptions: ModuleDescriptions | dict[str, str] | None=None, attributes: list[~pyfsr.models._modules_admin.AttributeMetadata | dict[str, ~typing.Any]]=<factory>, importedBy: list[Any] = <factory>, **extra_data: Any)[source]¶
Bases:
ModuleMetadataA staging module record from
/api/3/staging_model_metadatas.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Export / import¶
- class pyfsr.models.ConnectorSelection(*, value: str, label: str | None = None, version: str | None = None, include: bool = True, rpm: bool = True, configurations: bool = True, configCount: int = 0, recordCount: int = 0, **extra_data: Any)[source]¶
Bases:
_ExportEntryA connector selection (
options.connectors[]).value(thecyops-connector-<name>-<version>string) is the only field the engine keys on; a barenameis ignored.configurationstoggles whether the connector’s saved configs (secrets) ride along.- model_config = {'extra': 'allow'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ModuleSelection(*, value: str, includedAttributes: list[str] | None = None, **extra_data: Any)[source]¶
Bases:
_ExportEntryA module schema selection (
options.modules[]).valueis the module api name (e.g."alerts").includedAttributeslimits the exported fields; leaveNone(the default) to export the whole schema — the key is then omitted from the wire, which the export engine reads as “all attributes” (an explicit empty list, by contrast, exports none).- model_config = {'extra': 'allow'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.PlaybookCollectionSelection(*, value: str, label: str | None = None, include: bool = True, recordCount: int = 0, includeVersions: bool = True, includeSchedules: bool = True, includeGlobalVariables: bool = True, **extra_data: Any)[source]¶
Bases:
_ExportEntryA playbook-collection selection (
options.playbooks.collections[]).valueis the collection uuid. Theinclude*flags mirror the wizard’s Playbooks-step toggles for pulling the collection’s dependent content.- model_config = {'extra': 'allow'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RecordSet(*, type: str, query: dict[str, Any], label: str | None = None, include: bool = True, includeCorrelations: bool = False, **extra_data: Any)[source]¶
Bases:
_ExportEntryA filtered record-data export (
options.recordSets[]).querymust carry alimit(the record-export trigger — absent means the engine emits no records). The rest ofqueryis a standardpyfsr.query.Query.to_body()dict, so filtering works as elsewhere.- model_config = {'extra': 'allow'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ViewTemplateSelection(*, uuid: str, module: str, viewOptions: str | None = None, filters: list[Any] = [], **extra_data: Any)[source]¶
Bases:
_ExportEntryA view-template selection (
options.viewTemplates[]).Not a bare id: the export engine embeds the resolved
system_view_templatesrow for a module/layout, keyed byuuidand carryingmodule,viewOptions("list"/"detail"/"form"), and any recordfilters. Resolved live from the module’s templates atcreate_template()time (live-verified 8.0.0).- model_config = {'extra': 'allow'}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
System & platform¶
- class pyfsr.models.AIAgent(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, label: str | None = None, description: str | None = None, type: str | None = None, version: str | None = None, installed: bool | None = None, latestAvailableVersion: str | None = None, latestCompatibleVersion: str | None = None, fsrMinCompatibility: str | None = None, publisher: str | None = None, certified: bool | None = None, featured: bool | None = None, featuredTags: list[FeaturedTag] | None = None, draft: bool | None = None, local: bool | None = None, development: bool | None = None, dependencies: list[Any] | None = None, category: list[Any] | None = None, iconLarge: str | None = None, infoPath: str | None = None, publishedDate: float | None = None, buildNumber: int | None = None, configCount: int | None = None, status: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, recordTags: Any | None = None, importedBy: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
ContentHubItemA Content Hub AI agent (
type == "ai_agent"). FortiSOAR 8.0.0+.AI agents ship through the same Content Hub catalog as packs and connectors and are served by the same
/api/query/solutionpacksendpoint (@typeon the wire isSolutionPack) — only thetypediscriminator differs.nameis the agent id (e.g."conversation") andlabelits display name (e.g."Chat Assistant"); either resolves an agent viaget_installed_ai_agent().- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AggregateRow(**extra_data: Any)[source]¶
Bases:
ApiResultOne row of a server-side aggregation.
Returned by
aggregate(). The keys are the aliases supplied to that call — group-by fields keep the field’s last path segment, metrics use their explicit alias, andcount=Trueaddstotal— so the shape is entirely caller-defined and every key lives inextra. Dict-compatible (row["total"]works alongsidevalue());value()is just a typed accessor for one alias.Example:
rows = client.records("workflows").aggregate( group_by="triggerStep.stepType.name", count=True) rows[0]["name"], rows[0].value("total")
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ApiKey(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, title: str | None = None, userId: str | None = None, roles: list[str] | None = None, teams: list[str] | None = None, avatar: Any | None = None, recordTags: list[Any] | None = None, userType: Any | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, id: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordAn API-key binding record from
/api/3/api_keys/.This is the scope object that binds roles/teams to an API-key user (the user record carrying the key material, created via
/api/auth/users—ApiKeyUser). It is also an actor: it’s therecord_type == "ApiKey"subtype of the sharedactorstable, so a record created via an API key expands itscreateUser/modifyUserto this record (@type == "ApiKey", IRI/api/3/api_keys/<uuid>— live-verified on 8.0.0).@typeon the wire isApiKey; the module slug isapi_keys. The key value itself is masked on every read here ��� the plaintext lives on the API-key user, recoverable only at create time (or viashow_api_keywhenretrievable_modewas on).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ApiKeyMaterial(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, key: str | None = None, retrievable: bool | None = None, status: str | None = None, valid_until: int | None = None, time_remaining: int | None = None, modify_date: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordThe nested
api_keyblock on anApiKeyUser.Carries the key value (masked unless read with
show_api_keyunderretrievable_mode) and its validity/status metadata. Modeled as aBaseRecordsoak.get("key")/ak.get("retrievable")work — the plaintext-recovery helper inpyfsr.api.api_keysrelies on that.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ApiKeyUser(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, user_type: int | None = None, status: int | None = None, access_type: str | None = None, loginid: str | None = None, api_key: ApiKeyMaterial | None = None, bind_name: str | None = None, domain: str | None = None, is_logged_in: bool | None = None, tenant: Any | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordAn API-key user from
/api/auth/users(usersresp[0]).The user record that carries key material (
user_type == 9), linked to theApiKeybinding — which is the actual actor-table row (record_type == "ApiKey") that shows up oncreateUser/modifyUser. Distinct from a PeopleUser. This/api/auth/usersshape is not a JSON-LD/api/3collection (no@id/@typeon the wire), butBaseRecordworks fine:id_iri/record_typestayNoneand dict-access (u["uuid"],u.get("api_key")) keeps working. The nestedapi_keyis parsed intoApiKeyMaterial.- api_key: ApiKeyMaterial | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.Appliance(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, title: str | None = None, userType: Any | None = None, avatar: Any | None = None, userId: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, id: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA FortiSOAR appliance actor (
@type == "Appliance").One of the concrete subtypes of an actor: FortiSOAR stores all security principals in a single
actorstable using single-table inheritance keyed on therecord_typediscriminator (root-verified against the appliance’s Doctrine entities —PersonextendsActor, sameactorstable). AnApplianceis therecord_type == "Appliance"sibling of a humanUser(record_type == "Person"); it appears ascreateUser/modifyUseron records created by the playbook engine itself, wherenameis typically"Playbook". The/api/3/appliances/collection is the filtered view of these rows.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.Attachment(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, description: str | None = None, file: FileRecord | str | None = None, type: str | None = None, assignee: User | str | None = None, recordTags: list[str] | None = None, createUser: str | User | None = None, createDate: float | None = None, modifyUser: str | User | None = None, modifyDate: float | None = None, id: int | str | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordAn
/api/3/attachmentsrecord linking an uploadedFileRecord.Field set captured from a live 7.6.5
/api/3/attachmentsresponse.fileis the linkedFileRecord(the create response expands it; a bare IRI string is also accepted). Storage/audit/tenancy keys stay inextra.- file: FileRecord | str | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ConnectorOperation(*, operation: str | None = None, title: str | None = None, description: str | None = None, visible: bool | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne action a connector exposes, from its
info.jsonoperations[].Live-verified stable fields: the
operationslug, humantitle/description, and thevisibleflag. Operation-specific extras (parameters, output schema, category, …) stay inextra. Dict-compatible.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ConnectorVersionInfo(*, name: str | None = None, label: str | None = None, description: str | None = None, version: str | None = None, type: str | None = None, buildNumber: int | None = None, publishedDate: int | None = None, lastUpdated: int | None = None, publisher: str | None = None, certified: bool | None = None, category: str | None = None, infoPath: str | None = None, help: str | None = None, releaseNotes: str | None = None, availableVersions: list[str] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA connector’s published
info.jsonfrom Fortinet’s public Content Hub repo.Returned by
connector_versions(). This is the repo manifest ({repo}/.../latest/info.json), a different shape from the on-boxContentHubConnectorcatalog entry — most notably it carriesavailableVersions, every version ever published. Curated fields are typed; the rest (scm,help, icon paths, …) stay inextra. Dict-compatible, soinfo["availableVersions"]still works.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ContentHubConnector(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, label: str | None = None, description: str | None = None, type: str | None = None, version: str | None = None, installed: bool | None = None, latestAvailableVersion: str | None = None, latestCompatibleVersion: str | None = None, fsrMinCompatibility: str | None = None, publisher: str | None = None, certified: bool | None = None, featured: bool | None = None, featuredTags: list[FeaturedTag] | None = None, draft: bool | None = None, local: bool | None = None, development: bool | None = None, dependencies: list[Any] | None = None, category: list[Any] | None = None, iconLarge: str | None = None, infoPath: str | None = None, publishedDate: float | None = None, buildNumber: int | None = None, configCount: int | None = None, status: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, recordTags: Any | None = None, importedBy: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
ContentHubItemA Content Hub connector listing (
type == "connector").Named
ContentHubConnectorto avoid clashing with the liveclient.connectors(execution) surface — this is the catalog entry.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ContentHubItem(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, label: str | None = None, description: str | None = None, type: str | None = None, version: str | None = None, installed: bool | None = None, latestAvailableVersion: str | None = None, latestCompatibleVersion: str | None = None, fsrMinCompatibility: str | None = None, publisher: str | None = None, certified: bool | None = None, featured: bool | None = None, featuredTags: list[FeaturedTag] | None = None, draft: bool | None = None, local: bool | None = None, development: bool | None = None, dependencies: list[Any] | None = None, category: list[Any] | None = None, iconLarge: str | None = None, infoPath: str | None = None, publishedDate: float | None = None, buildNumber: int | None = None, configCount: int | None = None, status: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, recordTags: Any | None = None, importedBy: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordShared base for Content Hub items (solution packs, connectors, widgets).
Returned by
client.content_hubsearches. Stable, platform-owned schema (the marketplace catalog shape). Subclassed bySolutionPack,ContentHubConnector, andWidget, which add nothing of their own today — the catalog returns one flat shape discriminated bytype— but exist so callers canisinstance-narrow and so future per-type fields have a home.- featuredTags: list[FeaturedTag] | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.DailyActionCount(*, daily_action_limit: int | None = None, remaining_actions: int | None = None, reset_time: int | None = None, last_update_time: float | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultDaily action-count license usage —
client.system.daily_action_count().From
GET /api/wf/workflow/config/?section=license(the endpoint the UI’sgetDailyActionCountcalls). Counters are decrypted by the workflow engine.daily_action_limitis the per-day cap enforced by the license (e.g. 10000 on FortiFlex Starter);-1means unlimited/unenforced (e.g. an Evaluation or edition with no action cap).remaining_actionscounts down as counted steps run (Create/Update Record, Connector Action, Set Variable, …; Wait, Approval, Loops, and Reference-a-Playbook are not counted).reset_timeis the epoch second at whichremaining_actionsresets to the limit.- property enforced: bool¶
True when a positive daily cap is in force (
daily_action_limit > 0);-1/0 mean unlimited or unenforced.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.EmailTemplate(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, subject: str | None = None, content: str | None = None, visible: bool | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA FortiSOAR email template record from
/api/3/email_templates/.Reusable subject/body used by notification playbooks and the SMTP connector’s “Email Template” body type. The module slug is
email_templates;@typeon the wire isEmailTemplate.subjectandcontentmay contain Jinja that the platform expands at send time (verified against a live 8.0 box).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ExportConnectorRef(*, name: str | None = None, value: str | None = None, version: str | None = None, rpm: bool | None = None, rpm_name: str | None = None, rpmExists: bool | None = None, exists: bool | None = None, include: bool | None = None, includeInstall: bool | None = None, install_mode: str | None = None, installer_path: str | None = None, configurations: bool | None = None, configCount: int | None = None, configurationCount: int | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne connector entry in an export template’s
options.connectors.Field set captured from a live 7.6.5 export-template
options.connectors[]entry — every value is a scalar (str/bool/int).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ExportOptions(*, connectors: list[ExportConnectorRef] = [], **extra_data: Any)[source]¶
Bases:
ApiResultAn export template’s selection manifest (
export_template.options).connectorsis modeled (seeExportConnectorRef). The manifest’s other selection lists are preserved verbatim inextrarather than typed, because their element shapes have not been captured populated from live wire — they are added here as they are observed, never guessed.Live-verified on 8.0.0, an
optionsmanifest carries up to 25 category keys:actors,ai_agents,appSettings,connectors,dashboards,exportTemplates,externalTemplates,fixtures,mcp_configurations,modules,picklistNames,playbookBlocks,playbooks,postInstall,preInstall,preprocessingRules,recordSets,reports,roles,ruleChannels,rules,teams,viewTemplates,views,widgets.- connectors: list[ExportConnectorRef]¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ExportTemplate(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, options: ExportOptions | None = None, lastExportDate: float | None = None, type: str | None = None, createUser: str | User | None = None, createDate: float | None = None, modifyUser: str | User | None = None, modifyDate: float | None = None, id: int | str | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordAn
/api/3/export_templatesrecord — a reusable export selection.Field set captured from a live
/api/3/export_templatesresponse.optionsis the typedExportOptionsselection manifest.typedistinguishes the export kind — live-verified on 8.0.0 the values are"Export Wizard"(a normal config export) and"SolutionPack Export". Export bookkeeping (metadata,solutionPack) rides the wire but stays inextrauntil its element shape is captured populated.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- options: ExportOptions | None¶
- class pyfsr.models.FeaturedTag(*, tag: str | None = None, color: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA marketplace “featured” badge on a Content Hub item.
The
featuredTagsarray on aContentHubItemcarries these — live-verified shape is{"tag": "preview", "color": "#2d87e3"}(the label and the hex colour the catalog UI renders the chip with). Dict-compatible, sotag["tag"]works alongsidetag.tag.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.FileRecord(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, filename: str | None = None, mimeType: str | None = None, size: int | None = None, uploadDate: float | None = None, thumbnail: Any | None = None, assignee: str | None = None, file: Any | None = None, metadata: Any | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, id: int | str | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA
/api/3/filesrecord, returned byupload().Stable platform schema. The
@idIRI (rec.iri) is what attachment, import, and similar payloads reference as theirfilefield.filenameandmimeTypeare the most-used typed fields; the rest of the storage metadata (size, content path, thumbnails) stays inextra.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ImportJob(*, id_iri: str | None = None, uuid: str | None = None, status: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe async import job embedded in a solution-pack install response.
POST /api/3/solutionpacks/installreturns the pack entity with this object tracking the install; itsuuidis whatinstall_status()andwait_for_install()poll. Dict-compatible, sojob["uuid"]works alongsidejob.uuid.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ManualInput(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, id: int | None = None, record: str | None = None, type: str | None = None, title: str | None = None, external_channel_list: list[Any] | None = None, inline_channel_list: list[Any] | None = None, owners: list[Any] | None = None, assignment_type: str | None = None, owner_details: dict[str, Any] | None = None, created: str | None = None, timeout: Any | None = None, timeout_details: Any | None = None, step_id: int | None = None, unauthenticated_input: bool | None = None, agent_id: str | None = None, is_approval: bool | None = None, workflow: str | int | None = None, input: ManualInputForm | None = None, response_mapping: ResponseMapping | None = None, custom_fields: dict[str, Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA pending manual workflow input from
/api/wf/api/manual-wf-input/.A playbook paused on a Manual Input / Approval step, waiting on a human. This is a
wfAPI entity, not a/api/3module, so there is no JSON-LD envelope –id(int) is the identity andid_iri/record_typestayNone. Field set captured from a live 8.0 box.workflowis the encrypted run token (Fernet),step_idthe paused step, andis_approvaldistinguishes an approval gate from a data-input prompt.assignment_type/owners/owner_detailsdescribe who the input is assigned to.titleis the prompt’s schema title – the Manual Input step’stitle:, mirrored frominput.schema.title– not the step name. They coincide only when the step declares notitle:, in which case the schema title defaults to the step name.- input: ManualInputForm | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- response_mapping: ResponseMapping | None¶
- class pyfsr.models.ManualInputForm(*, schema: ManualInputSchema | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe
inputobject of a retrieved Manual Input: wraps the form schema.The wire key is
schema; it is exposed as theschema_attribute (schemashadowsBaseModel.schema) but stays reachable by its wire name through dict access –form["schema"]returns the typedManualInputSchema.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- schema_: ManualInputSchema | None¶
- class pyfsr.models.ManualInputOption(*, option: str | None = None, step_iri: str | None = None, primary: bool | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne response button of a Manual Input (
response_mapping.options[]).optionis the button label;step_irithe workflow step the run routes to when chosen (an/api/3/workflow_steps/<uuid>IRI);primarymarks the default/highlighted button (absent on plain buttons). Live-verified fromretrieve_wfinput.step_iriis wired at author time from the step’snext:, so a Manual Input step with no next step yields an option without one. Such a run cannot be resumed –wfinput_resume500s on a null or absentstep_iri– whichanswer()reports up front.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ManualInputResume(*, task_id: str | None = None, message: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe ack from resuming a manual input (
.../wfinput_resume/).Live-verified shape:
task_id(the async resume task) plus the step’smessage(e.g."Awaiting Playbook resumed successfully."). Dict-compatible, soresp["task_id"]works alongsideresp.task_id.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ManualInputSchema(*, title: str | None = None, description: str | None = None, inputVariables: list[ManualInputVariable] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe form schema of a Manual Input prompt (
input.schema).Live-verified:
title/descriptionare the prompt header, andinputVariablesthe ordered list of fields the user fills in (empty for a button-only / DecisionBased prompt).- inputVariables: list[ManualInputVariable] | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ManualInputVariable(*, name: str | None = None, type: str | None = None, label: str | None = None, title: str | None = None, tooltip: str | None = None, dataType: str | None = None, formType: str | None = None, required: bool | None = None, options: list[Any] | None = None, defaultValue: Any | None = None, templateUrl: str | None = None, playbookField: bool | None = None, jinjaExpressionView: bool | None = None, useRecordFieldDefault: bool | None = None, usable: bool | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne field in a Manual Input prompt’s collected form (
inputVariables[]).Field set captured from a live
retrieve_wfinputresponse: a friendlyinputs:field compiles to this canonical shape.nameis the variable referenced after resume asvars.steps.<step>.input.<name>;formType/dataType/type/templateUrldrive how FortiSOAR renders and validates the widget (e.g.formType="dynamicList"withoptionsis a select;requiredgates submission).optionsis present only for the list widgets. Unknown/internal keys (_expanded,_previousName, …) ride through viaextra="allow".- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ModulePermission(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, canCreate: bool | None = None, canRead: bool | None = None, canUpdate: bool | None = None, canDelete: bool | None = None, canExecute: bool | None = None, fieldPermissions: list[Any] | None = None, module: Any | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordOne module’s CRUD/execute grant inside a
Role.Live-verified shape (
@type == "ModulePermission"): the fivecan*booleans, an optionalfieldPermissionslist, and amodulerelationship (an IRI string, or the expanded module object when relationships are pulled). Dict-compatible, soperm["canRead"]works alongsideperm.canRead.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Bases:
BaseRecordThe “app” navigation view (
GET /api/views/1/app).A single view record (not a collection) describing the left-hand navigation.
config["navigation"]is the list of top-level sections, each a dict with atitleand optional nesteditems— those titles are what theviewsexport category ships. Usenavigation_sections()to read them without walkingconfigby hand.Note this endpoint returns the view without a JSON-LD envelope, so
iriisNone;uuididentifies it.Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Titles of the top-level navigation sections, in display order.
Top-level navigation sections from
config["navigation"]([]if absent).
- class pyfsr.models.Notification(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, content: str | None = None, footer: list[Any] | None = None, entity_type: str | None = None, event_type: str | None = None, entity_id: str | None = None, read: bool | None = None, dismissible: bool | None = None, created_on: str | None = None, roles: list[Any] | None = None, user: str | None = None, teams: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA FortiSOAR system notification from
/api/rule/api/system-notification/notifications/.The per-user bell-icon notifications the platform raises for record events (task assignments, approvals, SLA breaches, …). This is a
ruleAPI entity, not a/api/3module, so there is no JSON-LD envelope —uuidis the identity andid_iri/record_typestayNone. The listing is fetched with POST (seeNotificationsAPI), not GET. Field set re-verified against a live 8.0.0 box (no drift).contentis the rendered HTML shown in the notification panel;entity_type/entity_idpoint at the record the event fired on (e.g."tasks"plus a uuid), andevent_typeis the action ("create"/"update"/ …).read/dismissibledrive the panel’s unread badge and dismiss control.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.NotificationPurge(*, result: str | None = None, status: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe ack from a system-notification purge (
.../system-notification/purge/).Live-verified shape:
result(human message) andstatus(e.g."started"– the purge runs asynchronously). Dict-compatible.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.PicklistItem(*, id_iri: str | None = None, uuid: str | None = None, itemValue: str | None = None, listName: str | dict[str, Any] | None = None, orderIndex: int | None = None, color: str | None = None, icon: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne item (option) of a picklist, from
GET /api/3/picklistsor a create.The bulk listing returns every item across every picklist in one page; each carries its own
@id(the IRI the API stores on records), its friendlyitemValue, and thelistNameIRI of the picklist it belongs to. Map thatlistNameIRI to a name viaGET /api/3/picklist_names. Curated fields are typed (itemValue/order_index/color/icon); the rest of the JSON-LD envelope rides through inextra. Dict-compatible.order_indexis the wireorderIndex(the int sort key). The legacyordinalattribute is kept as a read alias so existing callers keep working.- property list_name_iri: str | None¶
The owning picklist’s
listNameIRI, whether it arrived as a string or an expanded{@id: ...}dict.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- property ordinal: int | None¶
Legacy alias for
order_index(the wire field isorderIndex).
- class pyfsr.models.PicklistName(*, id_iri: str | None = None, uuid: str | None = None, name: str | None = None, system: bool | None = None, picklists: list[PicklistItem] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA picklist list (the taxonomy an option belongs to), from
GET /api/3/picklist_namesor a create.Each list carries a friendly
name(unique instance-wide — a duplicate POST 409s withUniqueConstraintViolationException), asystemflag, and itspicklistsitems (embedded only when the request asks for$relationships=true; absent/empty otherwise).iriis the/api/3/picklist_names/<uuid>an option’slistNamepoints back at. Dict-compatible; the JSON-LD envelope (@context/@type/id/importedBy) rides through inextra.- property iri: str | None¶
The list’s IRI (
/api/3/picklist_names/<uuid>) — what an option’slistNamefield references.
- property items: list[PicklistItem]¶
The list’s options (embedded under
$relationships=true); empty when not expanded or the list has none.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- picklists: list[PicklistItem] | None¶
- class pyfsr.models.PostInstallConfig(*, enabled: bool | None = None, widgets: list[PostInstallWidget] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA solution pack’s post-install action (
infoContent.postInstallConfig).Live shape
{"enabled": true, "widgets": [{...}]}—enabledmirrors the wizard’s Configure post-install action checkbox andwidgetsholds the widget(s) to offer after install (the wizard authors exactly one). There is no matching pre-install action in the wizard; thepreInstall/postInstallexport-manifest keys are unrelated install-time scripts. Dict-compatible.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- widgets: list[PostInstallWidget] | None¶
- class pyfsr.models.PostInstallWidget(*, name: str | None = None, label: str | None = None, version: str | None = None, buttonLabel: str | None = None, autoLaunch: bool | None = None, autoLaunchTriggered: bool | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne widget in a solution pack’s post-install action.
A pack’s Configure post-install action offers a widget the operator can launch once the pack is installed. Shape captured from a live pack’s
info.jsonand confirmed against the 8.0.0 editor’s solution-pack metadata wizard: the dropdown setsname/label/versiontogether, and the two controls beside it setbuttonLabel(the launch button’s text, required when the action is enabled) andautoLaunch(“Launch automatically the first time”).autoLaunchTriggeredis runtime-only — the install flow sets it after the first auto-launch — and is never authored. Dict-compatible.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.QueryDefinition(*, logic: str = 'AND', filters: list[QueryFilter] = <factory>, limit: int | None = None, page: int | None = None, search: str | None = None, sort: list[Any] | None = None, aggregates: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe
querybody of aSystemQuery(also the shape you POST to/api/query/<module>).Warning
logicis load-bearing. Omit it and FortiSOAR drops every filter on the floor and returns the whole module — no error, no warning. Same for a filter missingQueryFilter.type. Live-verified on 8.0.0:{"filters": [{"field": "source", "operator": "eq", "value": "nope"}]} -> ALL records {"logic": "AND", "filters": [ {"field": "source", "operator": "eq", "value": "nope", "type": "primitive"}]} -> 0 records
Never delete straight from a query result without re-checking each record client-side.
- filters: list[QueryFilter]¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.QueryFilter(*, field: str | None = None, operator: str | None = None, value: Any | None = None, type: str | None = None, logic: str | None = None, filters: list[QueryFilter] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne condition inside a
SystemQuery’squery.filters.typeis not cosmetic and must not be dropped: FortiSOAR silently ignores a filter that omits it (and silently ignores every filter when the enclosing body omitslogic), returning all records rather than an error — seeQueryDefinition. Useprimitivefor scalars,objectfor picklist/IRI values,datetimefor dates.A nested group sets
logic+filtersinstead offield/value.- filters: list[QueryFilter] | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RepoConnectorEntry(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, label: str | None = None, description: str | None = None, type: str | None = None, version: str | None = None, installed: bool | None = None, latestAvailableVersion: str | None = None, latestCompatibleVersion: str | None = None, fsrMinCompatibility: str | None = None, publisher: str | None = None, certified: bool | None = None, featured: bool | None = None, featuredTags: list[FeaturedTag] | None = None, draft: bool | None = None, local: bool | None = None, development: bool | None = None, dependencies: list[Any] | None = None, category: str | list[Any] | None = None, iconLarge: str | None = None, infoPath: str | None = None, publishedDate: float | None = None, buildNumber: int | None = None, configCount: int | None = None, status: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, recordTags: Any | None = None, importedBy: list[Any] | None = None, path: str | None = None, rpm_name: str | None = None, rpm_full_name: str | None = None, icon: str | None = None, **extra_data: Any)[source]¶
Bases:
ContentHubItemOne entry from the public
connectors.jsonmanifest (repo.fortisoar.fortinet.com/connectors/info/connectors.json).Returned by
pyfsr.repo.list_connectors()/pyfsr.repo.search_connectors()— the no-appliance catalog, distinct from the on-boxContentHubConnector(which needs an appliance). The manifest is latest-version-only per connector and carries the RPM packaging fields the catalog entry doesn’t; those are typed here. The catalog-shaped fields (name/label/version/description/category) come fromContentHubItem. Dict-compatible.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.Report(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, displayName: str | None = None, type: str | None = None, templateType: str | None = None, parentTemplateId: str | None = None, config: dict[str, Any] | None = None, filterArray: list[Any] | None = None, importedBy: list[Any] | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, updateDate: float | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA report (
GET /api/3/reporting).The report definitions behind the SOAR UI’s Reports section. Note the display name is
displayName, notname— there is nonamefield on this entity, which is why report lookups match ondisplayName.configholds the report layout/definition andfilterArrayits saved filters.templateTypedistinguishes shipped templates from user-authored reports;parentTemplateIdlinks a report back to the template it was cloned from (Nonefor originals).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ResponseMapping(*, options: list[ManualInputOption] | None = None, duplicateOption: bool | None = None, customSuccessMessage: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA Manual Input’s response options + post-resume messaging (
response_mapping).Live-verified:
optionsare the buttons,duplicateOptionthe allow-duplicate flag,customSuccessMessagethe toast shown on resume.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- options: list[ManualInputOption] | None¶
- class pyfsr.models.ReusableBlock(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, description: str | None = None, type: str | None = None, reusable: bool | None = None, hasTriggerStep: bool | None = None, hideInLogs: bool | None = None, recordTags: list[str] | None = None, metadata: dict[str, Any] | list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA reusable playbook block — a
workflow_groupsrow withreusable=true.The saved, re-droppable step group surfaced in the playbook editor and the Configuration Export wizard’s Playbook Blocks category. From
GET /api/3/workflow_groups?reusable=true(live-verified 8.0).- metadata: dict[str, Any] | list[Any] | None¶
Editor canvas metadata; a bare
[]when unset (live-verified 8.0).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.Role(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, description: str | None = None, modulePermissions: list[ModulePermission] | None = None, importedBy: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA FortiSOAR role record from
/api/3/roles/.A role bundles module permissions and is assigned to users. The module slug is
roles;@typeon the wire isRole.modulePermissionsis only populated when the record is fetched with$relationships=true(verified against a live 7.6.5 box).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- modulePermissions: list[ModulePermission] | None¶
- class pyfsr.models.SolutionPack(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, label: str | None = None, description: str | None = None, type: str | None = None, version: str | None = None, installed: bool | None = None, latestAvailableVersion: str | None = None, latestCompatibleVersion: str | None = None, fsrMinCompatibility: str | None = None, publisher: str | None = None, certified: bool | None = None, featured: bool | None = None, featuredTags: list[FeaturedTag] | None = None, draft: bool | None = None, local: bool | None = None, development: bool | None = None, dependencies: list[Any] | None = None, category: list[Any] | None = None, iconLarge: str | None = None, infoPath: str | None = None, publishedDate: float | None = None, buildNumber: int | None = None, configCount: int | None = None, status: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, recordTags: Any | None = None, importedBy: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
ContentHubItemA Content Hub solution pack (
type == "solutionpack").- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.SolutionPackInfo(*, name: str | None = None, label: str | None = None, version: str | None = None, description: str | None = None, availableVersions: list[str] | None = None, dependencies: list[Any] | None = None, fsrMinCompatibility: str | None = None, category: str | list[Any] | None = None, publisher: str | None = None, certified: bool | None = None, postInstallConfig: PostInstallConfig | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA solution-pack’s published
info.jsonfrom Fortinet’s public content repo.Returned by
pyfsr.repo.solution_pack_info(). CarriesavailableVersions(full publish history) plusdependenciesandfsrMinCompatibility. Note there is no public manifest for solution packs and slug resolution is unreliable, so discovery (name -> slug) still needspyfsr.api.content_hub.ContentHubSearch.search_available_packs()on an appliance; this function is the per-version detail lookup once you know the slug. Curated fields are typed; the rest (contents,prerequisite,recordTags,featuredTags, …) stays inextra. Dict-compatible.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- postInstallConfig: PostInstallConfig | None¶
- class pyfsr.models.SolutionPackInstallResponse(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, label: str | None = None, description: str | None = None, type: str | None = None, version: str | None = None, installed: bool | None = None, latestAvailableVersion: str | None = None, latestCompatibleVersion: str | None = None, fsrMinCompatibility: str | None = None, publisher: str | None = None, certified: bool | None = None, featured: bool | None = None, featuredTags: list[FeaturedTag] | None = None, draft: bool | None = None, local: bool | None = None, development: bool | None = None, dependencies: list[Any] | None = None, category: list[Any] | None = None, iconLarge: str | None = None, infoPath: str | None = None, publishedDate: float | None = None, buildNumber: int | None = None, configCount: int | None = None, status: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, recordTags: Any | None = None, importedBy: list[Any] | None = None, importJob: ImportJob | None = None, **extra_data: Any)[source]¶
Bases:
SolutionPackThe SolutionPack record returned by
POST /api/3/solutionpacks/install.The install response is the full SolutionPack entity with an embedded
ImportJobtracking the async install. Usejob_idto get the UUID forinstall_status()andwait_for_install()calls.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.SystemQuery(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, models: str | dict[str, Any] | None = None, query: QueryDefinition | None = None, assignee: str | None = None, advanced: Any | None = None, resultCacheSeconds: int | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA saved dataset from
/api/3/system_queries/.A system query is a named, module-scoped filter — what the UI calls a dataset. Beyond driving saved views, a dataset on
threat_intel_feedsis a TAXII collection: the collection id served at/api/taxii/1/collections/<id>/objectsis this record’suuid(live-verified on 8.0.0). That is how FortiSOAR publishes an outgoing threat feed — seeTaxiiAPI.modelsis the target module’smodel_metadatasIRI (expanded to the full object on read).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- query: QueryDefinition | None¶
- class pyfsr.models.SystemViewTemplate(*, id_iri: str | None = None, uuid: str | None = None, name: str | None = None, module: str | None = None, viewOptions: str | None = None, type: str | None = None, isDefault: bool | None = None, system: bool | None = None, visible: bool | None = None, config: dict[str, Any] | list[Any] | None = None, filters: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA
system_view_templatesrow — a module/layout’s view configuration.From
GET /api/3/system_view_templates(bulk list) orGET /api/views/1/{name}(single named template), used byViewTemplatesAPI. A “default” is not a separate resource — it’s this row’sisDefaultflag, exactly one of which isTrueper(module, viewOptions)pair (verified live, 8.0).Template names are not unique across layouts: a module ships one “Default Layout” row per
viewOptions(list/detail/form), so resolving a template by name alone must also scope byviewOptions(seeUserSettingsAPI.resolve_view_template, which learned this the hard way).config(the layout body — rows/columns/widgets) is typed loosely since its shape varies bytype; the JSON-LD envelope (@context/@type) rides throughextra. Dict-compatible.- config: dict[str, Any] | list[Any] | None¶
The layout body (rows/columns/widgets); shape varies by
typeand is a bare[]for some modules’ empty layouts (live-verified 8.0).
- filters: list[Any] | None¶
Record filters scoping this view (the export wizard selects these); usually
[]for a system default layout.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.Team(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, description: str | None = None, importedBy: list[Any] | None = None, actors: list[User | Appliance | ApiKey | str] | None = None, parents: list[Any] | None = None, siblings: list[Any] | None = None, children: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA FortiSOAR team record from
/api/3/teams/.Teams own records (the
ownersrelationship) and scope visibility. The module slug isteams;@typeon the wire isTeam. The schema is deliberately slim — verified against a live 7.6.5 box, a team record carries onlyname/description/importedBybeyond the JSON-LD/uuid envelope.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.User(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, firstname: str | None = None, lastname: str | None = None, title: str | None = None, email: str | None = None, department: str | None = None, description: str | None = None, phoneWork: str | None = None, phoneMobile: str | None = None, phoneHome: str | None = None, phoneFax: str | None = None, csActive: bool | None = None, accessType: str | None = None, userType: Any | None = None, type: Any | None = None, avatar: Any | None = None, companyId: Any | None = None, userId: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, id: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA FortiSOAR user (
Person) record from/api/3/people/.A
Personis the human subtype of an actor. FortiSOAR keeps every security principal in oneactorstable via single-table inheritance keyed on therecord_typediscriminator (root-verified:Personextends the baseActorentity, sameactorstable); the sibling subtypes areAppliance(record_type == "Appliance") and theApiKeyactor (record_type == "ApiKey").@typeon the wire isPersonand the module slug ispeople— the/api/3/peoplecollection is the person-only view of the shared table, whereas/api/3/actorsspans all subtypes.This is the entity behind every
createUser/modifyUser/assignedTorelationship: when a record is pulled with relationships expanded those fields arrive as a full Person object, andBaseRecord.create_user()/modify_user()/assigned_to()parse them into this model (dispatching toAppliancewhen the expanded@typeisAppliance).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.Widget(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, label: str | None = None, description: str | None = None, type: str | None = None, version: str | None = None, installed: bool | None = None, latestAvailableVersion: str | None = None, latestCompatibleVersion: str | None = None, fsrMinCompatibility: str | None = None, publisher: str | None = None, certified: bool | None = None, featured: bool | None = None, featuredTags: list[FeaturedTag] | None = None, draft: bool | None = None, local: bool | None = None, development: bool | None = None, dependencies: list[Any] | None = None, category: list[Any] | None = None, iconLarge: str | None = None, infoPath: str | None = None, publishedDate: float | None = None, buildNumber: int | None = None, configCount: int | None = None, status: str | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, recordTags: Any | None = None, importedBy: list[Any] | None = None, **extra_data: Any)[source]¶
Bases:
ContentHubItemA Content Hub widget (
type == "widget").- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.WidgetInfo(*, name: str | None = None, title: str | None = None, subTitle: str | None = None, version: str | None = None, description: str | None = None, compatibility: list[str] | None = None, publisher: str | None = None, certified: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA widget’s published
info.jsonfrom Fortinet’s public content repo.Returned by
pyfsr.repo.widget_info(). Different shape from the connectorinfo.json��� the widget payload nests human fields under ametadatawrapper (which rides through inextra) and carries acompatibilitylist instead ofavailableVersions(a widgetinfo.jsonis per-version only; there is no public version-history manifest for widgets). Curated fields are typed; the rest stays inextra. Dict-compatible.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.Workflow(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, aliasName: str | None = None, tag: str | None = None, description: str | None = None, isActive: bool | None = None, debug: bool | None = None, singleRecordExecution: bool | None = None, remoteExecutableFlag: bool | None = None, synchronous: bool | None = None, triggerLimit: Any | None = None, parameters: list[str] | None = None, lastModifyDate: int | None = None, collection: str | None = None, triggerStep: str | None = None, priority: PicklistIRI | None = None, playbookOrigin: PicklistIRI | None = None, isEditable: bool | None = None, isPrivate: bool | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, deletedAt: float | None = None, importedBy: list[Any] | None = None, recordTags: list[str] | None = None, id: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA playbook (workflow) record from
/api/3/workflows/.Stable platform schema.
collectionis the IRI of the owningWorkflowCollection;triggerStepthe IRI of the start step.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- playbookOrigin: PicklistIRI | None¶
- priority: PicklistIRI | None¶
- class pyfsr.models.WorkflowCollection(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, description: str | None = None, visible: bool | None = None, image: Any | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, deletedAt: float | None = None, importedBy: list[Any] | None = None, recordTags: list[str] | None = None, workflows: list[Any] | None = None, id: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA playbook collection from
/api/3/workflow_collections/.The folder that groups playbooks; stable platform schema.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.WorkflowRun(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, status: str | None = None, created: str | None = None, modified: str | None = None, parent_wf: Any | None = None, tags: str | None = None, debug: bool | None = None, node_name: str | None = None, task_id: str | None = None, result: Any | None = None, template_iri: str | None = None, user: Any | None = None, steps: Any | None = None, env: Any | None = None, metadata: Any | None = None, peer_details: Any | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA playbook run record from
/api/wf/api/(historical-)workflows/.The raw run entity.
PlaybooksAPIalso exposes a flattened shape via its default (dict) return; passtyped=Truethere to get this model instead.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
AI & investigations¶
- class pyfsr.models.AgentConfig(*, config_type: str | None = None, llm_provider: str | None = None, mcp_server: list[str] = <factory>, masking_agent: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientThe inner
configof anAgentConfigDTO.mcp_serveris the per-agent MCP-server allowlist (uuids); an agent left on the default config reportsconfig_type == "default".- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AgentConfigDTO(*, agent_name: str | None = None, agent_version: str | None = None, name: str | None = None, default: bool = False, config: AgentConfig = <factory>, config_id: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientAiAgentConfigurationDTO– response of the agent-config endpoints (GET/POST /api/ai/agent/config/{name}/{version}and.../default).- config: AgentConfig¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AgentRecord(*, id: int | None = None, uuid: str | None = None, name: str | None = None, label: str | None = None, version: str | None = None, description: str | None = None, tags: list[str] = <factory>, category: str | None = None, active: bool | None = None, status: str | None = None, classpath: str | None = None, system: bool | None = None, installed: bool | None = None, inputformat: dict[str, ~typing.Any]=<factory>, outputformat: dict[str, ~typing.Any]=<factory>, config_schema: Any | None = None, configuration: list[Any] = <factory>, prompt: Any | None = None, additional_information: list[dict[str, ~typing.Any]]=<factory>, config_count: int | None = None, dependencies: list[Any] = <factory>, jailbreakguard: bool | None = None, llmconfig: Any | None = None, piimasking: bool | None = None, **extra_data: Any)[source]¶
Bases:
_LenientOne installed AI agent (
GET /api/ai/agent//GET .../{name}/{version}).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AgentRunResult(*, task_id: str | None = None, status: str | None = None, answer: Any | None = None, evidence: Any | None = None, confidence: str | None = None, logs: list[dict[str, ~typing.Any]]=<factory>, phases: list[dict[str, ~typing.Any]]=<factory>, **extra_data: Any)[source]¶
Bases:
_LenientResult of one single-agent run (
run_agent()).A single agent answers one question; it does not run the investigation pipeline, so this is a different shape from
InvestigationResult– the keys mirror the agent’s ownoutputformat(answer/evidence/confidence) rather thansummary/hypotheses.phasesis present but empty on a single-agent run; it is only populated for a full investigation. Live-verified on 8.0.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ConnectorMcpCandidates(*, available: list[str] = <factory>, restricted: list[str] = <factory>, **extra_data: Any)[source]¶
Bases:
_LenientWhich installed connectors can be hosted as an MCP server (
GET /mcp/servers/connector).restrictedconnectors (internal/system ones, e.g. the agent-communication bridge) can never be hosted.availableconnectors aren’t yet hosted – once one is, it drops off this list (find it instead viamcp_configs(), filtering ontype == "connector").- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.InvestigationHandle(*, task_id: str | None = None, status: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientResponse of starting/triggering a triage run –
{"task_id", "status"}.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.InvestigationQuestion(*, index: int | None = None, question: str | None = None, agent: str | None = None, input: Any | None = None, response: Any | None = None, evidence: str | None = None, supports: list[str] = <factory>, weakens: list[str] = <factory>, information_type: Any | None = None, status: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientOne question/evidence entry – see
investigation_questions().- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.InvestigationResult(*, task_id: str | None = None, status: str | None = None, summary: dict[str, ~typing.Any] | None=None, hypotheses: list[dict[str, ~typing.Any]]=<factory>, logs: list[dict[str, ~typing.Any]]=<factory>, **extra_data: Any)[source]¶
Bases:
_LenientFull triage result/verdict (
GET /api/ai/agents/{task_id}/result).summary/hypotheses/logsare left untyped (Any) – seeinvestigation_questions()andhypothesis_evidence()for the derived, typed views over this payload.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.LLMConfig(*, uuid: str | None = None, name: str | None = None, isdefault: bool | None = None, active: bool | None = None, model: str | None = None, modelname: str | None = None, provider: str | None = None, apikey: str | None = None, baseurl: str | None = None, config: dict[str, ~typing.Any]=<factory>, **extra_data: Any)[source]¶
Bases:
_LenientA reasoning-profile config (
GET /api/ai/llm/config), e.g. Low Reasoning.config.connector_name/connector_config_idpoint at the connector configuration backing this profile (e.g. thefortinet-fortiai-proxyproxy).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.LLMProvider(*, uuid: str | None = None, name: str | None = None, label: str | None = None, version: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientAn allowed LLM provider – an installed solution pack (
/api/ai/llm/allowed-providers).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.MCPServerConfig(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, url: str | None = None, transport: str | None = None, type: str | None = None, active: bool | None = None, timeout: int | None = None, command: str | None = None, authentication: str | dict[str, Any] | None = None, description: str | None = None, metadata: Any | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA registered MCP server (
/api/3/mcp_configurations– theMCPConfigurationmodule).authenticationis stored server-side as a JSON string (e.g.'{"type":"FSR"}'for built-ins,'{"value": "<bearer token>"}'for a remote server) – left untyped since its shape varies bytype. Seeregister_mcp_server()for the encode-on-write convenience andmcp_tool_catalog()for decoding it back to probetools/list.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.MCPServerRef(*, id: str | None = None, name: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientOne entry from
GET /api/ai/mcp– the id+name the agent-config UI lists.Thinner than
MCPServerConfig(no url/transport/auth); resolve to the full record viamcp_configs().- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.MCPServerStatus(*, uuid: str | None = None, name: str | None = None, valid: bool = False, error: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientOne entry from
GET /api/ai/mcp/status– the health of a registered MCP server.Complements
MCPServerRef(id+name) andMCPServerConfig(full record): this is the runtime liveness probe the agent UI uses to show green/red per server.validis the connectivity verdict;errorcarries the failure reason when it is not.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.MCPTool(*, name: str | None = None, description: str | None = None, inputSchema: dict[str, Any] | None = None, **extra_data: Any)[source]¶
Bases:
_LenientOne tool advertised by an MCP server’s
tools/list.Used both by the registration probe (
validate_mcp_server(), which reads the MCP-nativeinputSchemakey) and by the appliance’s own native gateway (list_tools(), whose historical dict shape usedinput_schema).inputSchemaaccepts either spelling on the wire andinput_schemareads it back either way, sotool["input_schema"]/tool.get("input_schema")andtool.inputSchemaall resolve – the dict-style access the tool-surface materializer relies on keeps working.- property input_schema: dict[str, Any] | None¶
Snake-case alias for
inputSchema(native-gateway dict shape).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.MCPToolResult(*, status: str | None = None, result: Any = None, error: Any = None, **extra_data: Any)[source]¶
Bases:
_LenientThe
{"status", "result", "error"}envelope a native gateway tool returns.Every FortiSOAR native tool (
/mcp/soc/,/mcp/playbooks/, …) replies with this envelope on success;okis a convenience forstatus == "success". In-band tool failures come back as a plain string instead of this envelope –call_tool()returns that raw value untouched, whilecall_tool_result()always wraps into this model (a non-envelope payload lands underresultwithstatus=None). Extra keys are preserved (extra="allow").- error: Any¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- property ok: bool¶
status == "success"– the FortiSOAR-native envelope convention.Only meaningful for FortiSOAR’s own tools (native gateway / internal registered servers), which reply with
{"status": "success", ...}. A third-party MCP server returns its own payload shape (e.g. raw text or its own JSON), sookisFalseeven on success – readresult/errorfor those.
- result: Any¶
- class pyfsr.models.MCPValidateResult(*, valid: bool = False, tools: list[MCPTool] = <factory>, message: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientResponse of
POST /api/ai/mcp/validate– probing a server before saving.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ToolCall(*, tool_name: str | None = None, tool_args: Any | None = None, correlation_id: str | None = None, title: str | None = None, model: str | None = None, latency_ms: int | None = None, server: str | None = None, server_uuid: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientOne MCP/connector tool invocation, from
llm_activity_logs– seetool_usage().- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Agents¶
- class pyfsr.models.Agent(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, agentId: str | None = None, name: str | None = None, active: bool | None = None, description: str | None = None, created: str | None = None, modified: str | None = None, router: RecordIRI | dict[str, Any] | None = None, installerType: PicklistIRI | None = None, configurationHealth: PicklistIRI | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordAn agent record from
GET /api/3/agentsorPOST /api/3/agents.Core fields typed; operational metadata (installer bytes, SME config, etc.) preserved in
extra.- configurationHealth: PicklistIRI | None¶
- installerType: PicklistIRI | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AgentConnectorStatus(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, agent: str | None = None, agentId: str | None = None, name: str | None = None, version: str | None = None, status: str | None = None, label: str | None = None, errorMessage: str | None = None, progressPercent: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA single row from
connector_install_status().Returned by
POST /api/integration/connectors/agents/<name>/<version>/.statusprogresses through"awaiting"→"in-progress"→"Completed".- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
AI agent packages¶
- class pyfsr.models.AgentInfo(*, name: str, label: str | None = None, agentclass: str | None = None, version: str = '1.0.0', description: str | None = None, publisher: str | None = None, cs_approved: bool | None = None, cs_compatible: bool | None = None, contributor: str | None = None, category: str | None = None, icon_small_name: str | None = None, icon_large_name: str | None = None, tags: list[str] = <factory>, fsrMinCompatibility: str | None = None, help_online: str | None = None, additional_information: list[dict[str, ~typing.Any]]=<factory>, inputformat: dict[str, ~typing.Any]=<factory>, outputformat: dict[str, ~typing.Any]=<factory>, configuration: dict[str, ~typing.Any]=<factory>, **extra_data: Any)[source]¶
Bases:
_LenientThe
info.jsonmanifest of an AI agent package.namemust match the package’s top-level folder, andagentclassmust name a class defined inagent.py;AgentPackagecross-checks both.configuration.fieldsis the per-agent config form the FortiSOAR UI renders (config-type toggle, LLM-provider picker, MCP-server multiselect, masking agent) — left untyped here as it’s a free-form field schema.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AgentMemory(*, allowed_tools: dict[str, list[str]]=<factory>, **extra_data: Any)[source]¶
Bases:
_Lenientconfig/memory.yaml— the agent’s MCP-tool allowlist.allowed_toolsmaps a registered MCP-configuration uuid (seeclient.ai.mcp_configs()) to the list of tool names on that server the agent may call. An empty list means “server is bound but no tools yet allowed”; the key must be a uuid that actually resolves on the target appliance or the binding is inert.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AgentPackage(*, info: AgentInfo, prompts: AgentPromptFile = <factory>, memory: AgentMemory = <factory>, files: list[str] = <factory>, agent_source: str | None = None)[source]¶
Bases:
BaseModelA fully-parsed AI agent package: manifest + prompts + memory + file list.
Build one with
from_dir()to validate a source folder before packing, or construct directly.validate_consistency()catches the mistakes that fail silently on the appliance rather than at upload:agent.pymissing, or not defining the class named byagentclass;a prompt uuid referenced in
agent.pythatprompt.yamldoesn’t define;icons named in the manifest that aren’t in the package.
- agent_source: str | None¶
Source of
agent.pywhen known — used to checkagentclassand cross-check referenced prompt uuids.
- classmethod from_dir(source_dir: str) AgentPackage[source]¶
Parse and validate an agent package from a source directory.
source_diris the package root (the folder that is the agent, e.g..../metric-computation). Readsinfo.json(required),prompt.yamlandconfig/memory.yaml(both optional), and records the file list +agent.pysource. Raises on a missing/invalid manifest or a failed consistency check.
- memory: AgentMemory¶
- model_config = {'arbitrary_types_allowed': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- prompts: AgentPromptFile¶
- validate_consistency() None[source]¶
Raise
ValueErroron package defects that fail silently on-box.Checks the
agentclassis defined inagent.py, every prompt uuid the source references exists inprompt.yaml, and manifest-named icons are present. A no-op for fields it can’t see (e.g. noagent_source).
- class pyfsr.models.AgentPrompt(*, name: str | None = None, system_instruction: str | None = None, user_instruction: str | None = None, validation_instruction: str | None = None, response_format: Any | None = None, description: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientOne entry in
prompt.yaml’spromptsmap (keyed by a uuid).agent.pypulls a prompt by that uuid (self.get_prompt_by_uuid(...)) and.format(**inputs)ssystem_instruction/user_instruction— so any{placeholder}in those strings must be supplied at call time.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.AgentPromptFile(*, prompts: dict[str, ~pyfsr.models._ai_agent_package.AgentPrompt]=<factory>, **extra_data: Any)[source]¶
Bases:
_LenientThe whole
prompt.yaml:{"prompts": {<uuid>: AgentPrompt}}.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- prompts: dict[str, AgentPrompt]¶
Rules¶
- class pyfsr.models.DeliveryRule(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, entity_type: str | None = None, event_type: str | None = None, event_source: str | None = None, trigger_condition: dict[str, Any] | None = None, actions: list[dict[str, Any]] | None = None, is_system: bool | None = None, is_active: bool | None = None, visible: bool | None = None, priority: int | None = None, category: str | None = None, source: Any | None = None, channel_preference_field: Any | None = None, expiry: Any | None = None, entity_id: Any | None = None, parent_rule: Any | None = None, workflow: Any | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA delivery rule from the rule engine (
GET /rule/api/rules/).The notification rules the SOAR UI lists under Rules: each pairs a
trigger_condition(a crudhub-style filter overentity_typerecords) withactions[]that fire on match, every action naming thechannel_uuidit delivers through (seeRuleChannel).Rule-engine objects carry no JSON-LD envelope, so
iriisNonehere —uuidis the only identifier.is_systemmarks the rules FortiSOAR ships; those exist on every appliance and are the safest ones to reference in a portable export template.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.PreprocessingRule(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, description: str | None = None, entityType: str | None = None, applicableOn: str | None = None, isActive: bool | None = None, priority: int | None = None, criteria: dict[str, Any] | None = None, action: dict[str, Any] | None = None, actionType: dict[str, Any] | str | None = None, endDate: float | None = None, skipPlaybookExecution: Any | None = None, recordTags: list[Any] | None = None, createUser: str | dict[str, Any] | None = None, createDate: float | None = None, modifyUser: str | dict[str, Any] | None = None, modifyDate: float | None = None, id: int | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA preprocessing rule (
GET /api/3/preprocessing_rules).Rules that run against records as they arrive (
applicableOn: "incoming") to dedupe, link, or update them before playbooks fire. UnlikeDeliveryRuleandRuleChannelthis is a crudhub record — JSON-LD envelope andcamelCasefields — soiriis populated.criteriaholds the matchcondition(plus adayslookback window) andactiondescribes what to do on match (link/update).- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RuleChannel(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, name: str | None = None, type: str | None = None, description: str | None = None, config: dict[str, Any] | None = None, is_active: bool | None = None, default_params: dict[str, Any] | None = None, **extra_data: Any)[source]¶
Bases:
BaseRecordA rule channel from the rule engine (
GET /rule/api/channel/).The delivery transport a
DeliveryRuleaction targets bychannel_uuid— e.g. In-App Notifications, email.typeis"system"for the built-in channels. LikeDeliveryRule, this is not a crudhub record: no JSON-LD envelope,uuidis the identifier.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Schedules¶
- class pyfsr.models.CrontabScheduleModel(*, id: int | None = None, minute: str | None = None, hour: str | None = None, day_of_month: str | None = None, month_of_year: str | None = None, day_of_week: str | None = None, timezone: str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultThe nested
crontabon aScheduledTask.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ScheduledTask(*, id: str | None = None, name: str | None = None, crontab: CrontabScheduleModel | None = None, interval: Any | None = None, task: str | None = None, args: str | None = None, kwargs: dict[str, Any] | None = None, queue: str | None = None, exchange: str | None = None, routing_key: str | None = None, headers: str | None = None, priority: Any | None = None, expires: str | None = None, expire_seconds: int | None = None, one_off: bool | None = None, start_time: str | None = None, enabled: bool | None = None, last_run_at: str | None = None, total_run_count: int | None = None, date_changed: str | None = None, description: str | None = None, solar: Any | None = None, clocked: Any | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA django-celery-beat
PeriodicTaskfrom/api/wf/api/scheduled/.idis a per-request Fernet token (not a stable primary key) — always look a task up bynamebefore writing it back, perSchedulesAPI’s module docstring.kwargscarries the workflow-specific payload (wf_iri,exit_if_running,schedule_id, …) and is left untyped since its shape varies by task.- crontab: CrontabScheduleModel | None¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Widgets¶
- class pyfsr.models.WidgetRecord(*, id_iri: str | None = None, uuid: str | None = None, name: str | None = None, version: str | None = None, title: str | None = None, subTitle: str | None = None, draft: bool | None = None, installed: bool | None = None, enablePublish: bool | None = None, metadata: dict | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultA widget record from
client.widgets(upload/publish/list/get).Dict-compatible, so
record["uuid"]works alongsiderecord.uuid. Fields not modeled here (tree, layout metadata, …) stay inextra.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
App configuration¶
Bases:
ApiResultA single navigation entry — a leaf or a group.
A leaf binds a module through
state/require. A group carries child entries underitemsand has nostate/require. All fields are optional because the wire shape differs between the two and between appliance versions; unknown keys are preserved for round-tripping.True if this entry has children (i.e. is a menu group).
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Bases:
ApiResultThe visibility gate on a navigation leaf.
{"module": "alerts", "action": "read"}means the leaf is shown only to users with thereadpermission on thealertsmodule. Groups carry norequire(it comes backNone/ absent), and an empty array[]on a leaf means unrestricted — seeNavItem.require.Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Bases:
ApiResultThe Angular UI-router state a navigation leaf routes to.
parametersis usually{"module": "<name>"}for module-list entries, but comes back as an empty list[]for parameterless states (e.g. the dashboard), so it is typed permissively.Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Base classes¶
- class pyfsr.models.BaseRecord(*, id_iri: str | None = None, record_type: str | None = None, uuid: str | None = None, **extra_data: Any)[source]¶
Bases:
BaseModelDict-compatible base for typed FortiSOAR records.
Every concrete entity model (Alert, Incident, Task, Comment, …) subclasses this. Modules without a registered model are parsed into a bare
BaseRecordso callers still get IRI/uuid helpers and dict access.- as_record(field: str, model: type[BaseModel]) Any[source]¶
Coerce relationship
fieldintomodel, whether expanded or an IRI.A single-relationship field comes back either as a bare IRI string (not expanded) or as the full nested object (relationships pulled). This normalizes both into a
modelinstance — an IRI string yields a thin instance carrying only@id(so.iriworks) — and returnsNonewhen the field is absent/null.
- property assigned_to: User | None¶
The assignee as a
User, orNone.Reads
assignedTo(alerts/incidents) and falls back toassignedToPerson(tasks).
- property create_user: Actor | None¶
The
createUseras aUserorAppliance.Dispatches on
@type:"Appliance"records (playbook-engine actors) return anAppliance,"ApiKey"records (a record created via an API key) return anApiKey, and everything else ("Person") returns aUser. All shareBaseRecordso.iriand.uuidalways work.
- get(key: str, default: Any = None) Any[source]¶
Dict-style accessor: value for
key(by name or@-alias) or default.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- property modify_user: Actor | None¶
The
modifyUseras aUserorAppliance.See
create_userfor dispatch logic.
- picklist_uuid(field: str) str | None[source]¶
Return the trailing uuid of a picklist/relationship IRI field.
Picklist and single-relationship fields hold an IRI like
/api/3/picklists/<uuid>; this pulls out the<uuid>tail. ReturnsNonewhen the field is absent or not a string IRI.
- to_dict(*, by_alias: bool = True, exclude_none: bool = False, serialize_special: bool = False) dict[str, Any][source]¶
Serialize back to a plain FortiSOAR-shaped dict.
Defaults to
by_alias=Trueso@id/@typeround-trip with their wire names.When
serialize_special=True, object/array fields (typed aslist[Any]ordict[str, Any]) are JSON-encoded to strings for wire submission, as FortiSOAR expects. WhenFalse(the default), they remain as native Python objects for backward compatibility.
_audit¶
- class pyfsr.models.AuditActivity(*, operation: str | None = None, transaction_date: int | None = None, user: str | None = None, user_id: str | None = None, playbook_name: str | None = None, playbook_iri: str | None = None, entity_type: str | None = None, entity_uuid: str | None = None, display_name: str | None = None, title: str | None = None, component: str | None = None, source: str | None = None, data: dict[str, Any] | None = None, link_entity_details: dict[str, Any] | None = None, id: int | str | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne audit-log entry — a single change event on a record.
Carries:
operation(Create/Update/Link/Unlink/Comment/Trigger/…),transaction_date(epoch ms),user("Playbook"for playbook changes),playbook_name/playbook_iri(when a playbook did it),entity_type,entity_uuid,title, anddata(linked entity details, old/new values, etc.).- property linked_entity_iri: str | None¶
The IRI of the entity linked/unlinked (
linkEntityDetails.iri), orNone.
- property linked_entity_type: str | None¶
The type of the linked entity (
indicators,assets…), orNone.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.ExecutionContext(*, run_pk: str | None = None, run_name: str | None = None, run_status: str | None = None, run_created: str | None = None, run_modified: str | None = None, record_uuid: str | None = None, record_iri: str | None = None, entity_type: str | None = None, concurrent_changes: list[LifecycleEntry] = [], concurrent_runs: list[dict[str, Any]] = [], before_changes: list[LifecycleEntry] = [], window_seconds: int = 60, **extra_data: Any)[source]¶
Bases:
ApiResultWhat was happening to a record around the time of a specific playbook run.
Returned by
client.audit.execution_context. Answers the debugging question “why did this playbook see state X when I expected state Y?” by showing what other playbooks or manual actions changed the record within the run’s time window.concurrent_changesare audit events on the same record that happened during the run (between itscreatedandmodifiedtimestamps, ± a buffer).concurrent_runsare other playbook executions on the same record in the same window.before_changesare audit events just before the run started (context for what state the playbook saw).- before_changes: list[LifecycleEntry]¶
- concurrent_changes: list[LifecycleEntry]¶
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.LifecycleEntry(*, timestamp_ms: int | None = None, kind: str | None = None, operation: str | None = None, user: str | None = None, playbook_name: str | None = None, title: str | None = None, entity_type: str | None = None, entity_uuid: str | None = None, linked_entity_iri: str | None = None, linked_entity_type: str | None = None, linked_entity_display: str | None = None, execution_pk: str | None = None, execution_status: str | None = None, raw: dict[str, Any] | None = None, **extra_data: Any)[source]¶
Bases:
ApiResultOne entry in a record’s lifecycle timeline (from
lifecycle()).A unified view of either an audit-log change or a playbook execution, sorted by timestamp.
kinddistinguishes the source:"audit"for a field change / link / comment,"execution"for a playbook run.- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.RecordLifecycle(*, entity_uuid: str | None = None, entity_type: str | None = None, entries: list[LifecycleEntry] = [], audit_count: int = 0, execution_count: int = 0, **extra_data: Any)[source]¶
Bases:
ApiResultThe full change-history timeline for a record (from
lifecycle()).Combines audit-log entries (field changes, links, comments) with playbook executions into a single sorted timeline.
entriesis oldest-first;by_playbook/state_changesprovide filtered views.- property by_playbook: list[LifecycleEntry]¶
Entries caused by a playbook (
user == "Playbook"or kind =="execution").
- property comments: list[LifecycleEntry]¶
Audit entries with operation
Comment.
- entries: list[LifecycleEntry]¶
- property field_changes: list[LifecycleEntry]¶
Audit entries with operation
Update(field-level changes).
- property links: list[LifecycleEntry]¶
Audit entries with operation
LinkorUnlink.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
_stix¶
- class pyfsr.models.StixAttackPattern(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, kill_chain_phases: list[dict[str, ~typing.Any]]=<factory>, external_references: list[dict[str, ~typing.Any]]=<factory>, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Attack Pattern SDO.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixBundle(*, type: str = 'bundle', id: str | None = None, objects: list[StixObject] = <factory>, **extra_data: Any)[source]¶
Bases:
_LenientA STIX 2.1 Bundle – the top-level container for STIX objects.
The object that FortiSOAR’s
POST /api/ingest-feeds/stix-bundleaccepts and that the TAXII objects endpoint can return (wrapped in theTaxiiObjectsEnvelope).- Variables:
type (str) – always
"bundle".id (str | None) – bundle identifier (
"bundle--<uuid>").objects (list[StixObject]) – the STIX objects inside, parsed into typed subclasses when possible (falls back to
StixObjectfor unknown types).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- objects: list[StixObject]¶
- class pyfsr.models.StixCampaign(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, aliases: list[str] = <factory>, first_seen: str | None = None, last_seen: str | None = None, objectives: str | None = None, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Campaign SDO.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixIndicator(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, pattern_type: str | None = None, valid_from: str | None = None, valid_until: str | None = None, labels: list[str] = <factory>, kill_chain_phases: list[dict[str, ~typing.Any]]=<factory>, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Indicator SDO.
- Variables:
pattern_type (str | None) – pattern language (
"stix","pcre").valid_from (str | None) – when the indicator is first considered valid.
valid_until (str | None) – when the indicator is no longer considered valid.
labels (list[str]) – open-vocab labels (e.g.
["malicious-activity"]).kill_chain_phases (list[dict[str, Any]]) – kill-chain phase entries.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixIntrusionSet(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, aliases: list[str] = <factory>, first_seen: str | None = None, last_seen: str | None = None, goals: list[str] = <factory>, resource_level: str | None = None, primary_motivation: str | None = None, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Intrusion Set SDO.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixMalware(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, is_family: bool | None = None, malware_types: list[str] = <factory>, kill_chain_phases: list[dict[str, ~typing.Any]]=<factory>, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Malware SDO.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixObject(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientBase for every STIX 2.1 object (SDO, SCO, SRO).
Carries the common STIX 2.1 properties. FortiSOAR’s TAXII server also stamps a scalar
value(the indicator value) and leavespatternnull, so those are exposed here rather than only onStixIndicator.- Variables:
type (str) – STIX object type (
"indicator","malware","threat-actor", …).id (str | None) – STIX identifier (
"<type>--<uuid>").spec_version (str | None) – STIX spec version (
"2.1").created (str | None) – creation timestamp (ISO 8601).
modified (str | None) – last-modified timestamp (ISO 8601).
name (str | None) – human-readable name.
description (str | None) – longer description.
value (str | None) – FortiSOAR’s scalar indicator value (an IP, hash, domain, etc.). Present on objects served from the TAXII endpoint; null on standard STIX SDOs that use
patterninstead.pattern (str | None) – STIX pattern expression. Typically null on FortiSOAR-served objects (
valuecarries the indicator instead).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixReport(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, published: str | None = None, report_types: list[str] = <factory>, object_refs: list[str] = <factory>, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Report SDO.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixThreatActor(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, threat_actor_types: list[str] = <factory>, aliases: list[str] = <factory>, first_seen: str | None = None, last_seen: str | None = None, kill_chain_phases: list[dict[str, ~typing.Any]]=<factory>, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Threat Actor SDO.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixTool(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, tool_types: list[str] = <factory>, kill_chain_phases: list[dict[str, ~typing.Any]]=<factory>, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Tool SDO.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.StixVulnerability(*, type: str, id: str | None = None, spec_version: str | None = None, created: str | None = None, modified: str | None = None, name: str | None = None, description: str | None = None, value: str | None = None, pattern: str | None = None, cve: str | None = None, external_references: list[dict[str, ~typing.Any]]=<factory>, **extra_data: Any)[source]¶
Bases:
StixObjectA STIX 2.1 Vulnerability SDO.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
_taxii¶
- class pyfsr.models.StixBundleResult(*, status: str | None = None, message: str | None = None, objects_processed: int | None = None, **extra_data: Any)[source]¶
Bases:
_LenientResponse of
POST /api/ingest-feeds/stix-bundle.Distinct from
FeedIngestResult: the STIX bundle endpoint returnsmessageandobjects_processedinstead of auuidslist, because the bundle fans out into multiple record types.- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.TaxiiCollection(*, id: str | None = None, title: str | None = None, description: str | None = None, can_read: bool | None = None, can_write: bool | None = None, media_types: list[str] = <factory>, **extra_data: Any)[source]¶
Bases:
_LenientOne TAXII collection entry.
Served both in the collection list and as a single-collection response.
- Variables:
id (str | None) – collection identifier (maps to a
SystemQueryuuid on FortiSOAR – a dataset is a TAXII collection).title (str | None) – human-readable title.
description (str | None) – longer description.
can_read (bool | None) – caller may read objects from this collection.
can_write (bool | None) – caller may add objects to this collection (typically
Falseon FortiSOAR – publishing is viasystem_queries).media_types (list[str]) – accepted media types (
["application/stix+json;version=2.1"]).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.TaxiiDiscovery(*, title: str | None = None, description: str | None = None, default: str | None = None, versions: list[str] = <factory>, max_content_length: int | None = None, **extra_data: Any)[source]¶
Bases:
_LenientTAXII discovery response (
GET /api/taxii/1/).The server descriptor clients call first to confirm protocol compatibility.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.TaxiiManifest(*, objects: list[TaxiiManifestEntry] = <factory>, totalItems: int | None = None, **extra_data: Any)[source]¶
Bases:
_LenientCollection manifest response (
GET .../collections/{id}/manifest).One entry per object, no bodies. Cheap “what’s new since X” poll.
- Variables:
objects (list[TaxiiManifestEntry]) – manifest entries (metadata for each object in the collection).
total_items (int | None) – total count (FortiSOAR-specific, may be absent).
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- objects: list[TaxiiManifestEntry]¶
- class pyfsr.models.TaxiiManifestEntry(*, id: str | None = None, date_added: str | None = None, version: str | None = None, media_type: str | None = None, **extra_data: Any)[source]¶
Bases:
_LenientOne entry in a collection manifest – metadata only, no object body.
- Variables:
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class pyfsr.models.TaxiiObjectsEnvelope(*, totalItems: int | None = None, objects: list[StixObject] = <factory>, **extra_data: Any)[source]¶
Bases:
_LenientSTIX objects envelope from a TAXII collection.
FortiSOAR’s non-standard
{totalItems, objects: []}wrapper (no TAXII 2.1more/nextcursor). Paginate withlimit+added_after.- Variables:
total_items (int | None) – total object count in the collection.
objects (list[StixObject]) – STIX objects, parsed into typed subclasses when possible.
- model_config = {'extra': 'allow', 'populate_by_name': True, 'validate_by_alias': True, 'validate_by_name': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- objects: list[StixObject]¶