Skip to content
Try the API

Investigate with Pursuit

Elastio Pursuit investigates copies of your systems off-host for evidence of compromise, such as persistence mechanisms, without touching the running machine. Use the API to check whether any host is under suspicion, read the persistence reports, start a Pursuit investigation, and give Pursuit context about an asset.

  • A personal access token with access to Pursuit and Hunt findings. Starting an investigation needs write access. Pursuit exceptions are admin-only, for reads as well as writes. See Authentication.
  • Pursuit runs on Windows only: on AWS, and on Azure VMs and managed disks for tenants with Pursuit on Azure enabled. The pursuit_supported field on GET /gateway/api/v2/assets/{assetId} tells you whether an asset qualifies.

The summary rolls up every host’s latest Pursuit report in a time window. By default that’s the last 30 days, and the window can’t exceed 180 days. state is SUSPECTED when at least one host’s latest report has a low, medium, high or critical suspicion level, and CLEAR otherwise. all_clear only means no host currently has such a report. It doesn’t mean the estate is proven clean.

GET
/gateway/api/v1/pursuit/summaryGet the estate Pursuit summary
Try it
Terminal window
curl "https://acme.app.elastio.com/gateway/api/v1/pursuit/summary" \
-H "Authorization: Bearer $ELASTIO_TOKEN"
{
"state": "SUSPECTED",
"all_clear": false,
"affected_host_count": 1,
"standalone_asset_ids": ["6f1c2a9e-3b4d-4e8f-9a7b-1c2d3e4f5a6b"],
"incident_count": 0,
"confirmed_lateral_incident_count": 0,
"primary_incident_id": null,
"active_incident_id": null,
"lateral_asset_ids": [],
"suspicion_asset_ids": []
}

Lateral movement and incidents aren’t modeled yet. The incident counts are always 0, the incident IDs are always null, lateral_asset_ids and suspicion_asset_ids are always empty, and state is never CONFIRMED_LATERAL. The affected hosts are in standalone_asset_ids.

For a time-ordered view, GET /gateway/api/v1/pursuit/timeline (Pursuit timeline) returns one item per host whose latest report in the window is medium, high or critical, with severity, time_utc, finding_count and a one-sentence summary.

List reports in a window (default 30 days, max 180), one per Pursuit investigation. Page with page_size (default 50, max 500) and page_token.

GET
/gateway/api/v1/pursuit/reportsList Pursuit persistence reports
Try it
Terminal window
curl -G "https://acme.app.elastio.com/gateway/api/v1/pursuit/reports" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
--data-urlencode "start_date=2026-09-12T00:00:00Z" \
--data-urlencode "page_size=100"
{
"reports": [
{
"report_id": "b7c6d5e4-f3a2-4b1c-9d0e-8f7a6b5c4d3e",
"asset_ref": { "id": "6f1c2a9e-3b4d-4e8f-9a7b-1c2d3e4f5a6b", "kind": "ec2_instance", "name": "fileserver-02" },
"generated_utc": "2026-10-11T04:12:00Z",
"highest_suspicion_anchor_utc": "2026-10-10T23:47:31Z",
"ingested_utc": "2026-10-11T04:13:05Z",
"suspicion_level": "high",
"summary": "A scheduled task added after the last clean copy launches an unsigned binary from a user-writable path.",
"finding_count": 3,
"image_id": null
}
],
"pagination": { "next_page_token": "", "prev_page_token": "", "total_count": 1 }
}

The page token is meant for follow-up requests, which is why the Python loop sends only page_token after the first page.

Open one report with GET /gateway/api/v1/pursuit/reports/{report_id}. It returns the engine’s report document as-is, with sections such as report_metadata, overall_assessment, findings, evidence and attack_timeline_reconstruction, plus text_report, the readable .txt analysis. A section the engine didn’t produce is left out. The spec only types text_report. Treat the other sections as engine-defined JSON.

GET
/gateway/api/v1/pursuit/reports/{report_id}Get one Pursuit persistence report
Try it
Terminal window
curl "https://acme.app.elastio.com/gateway/api/v1/pursuit/reports/b7c6d5e4-f3a2-4b1c-9d0e-8f7a6b5c4d3e" \
-H "Authorization: Bearer $ELASTIO_TOKEN"
{
"report_metadata": { "…": "…" },
"overall_assessment": { "…": "…" },
"findings": [ "…" ],
"text_report": "…"
}

Start a Pursuit investigation through the same endpoint that starts a hunt (Schedule hunts and investigations): set pursuit: true in scan_type, or name a playbook in pursuit_playbook. Give exactly one of built_in (for example persistence) or custom.

POST
/gateway/api/v1/scans/scheduleSchedule hunts and investigations
Try it
Terminal window
curl -X POST "https://acme.app.elastio.com/gateway/api/v1/scans/schedule" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scans": [
{
"type": "ec2",
"aws_instance_id": "i-0abc123def4567890",
"aws_account_id": "123456789012",
"aws_region": "us-east-1",
"scan_type": { "pursuit_playbook": { "built_in": "persistence" } }
}
]
}'
{ "results": [ { "result": "job_id", "job_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d", "resource": "aws_ec2" } ] }

To poll the job, see Run a hunt. When it finishes, read the results in one of three ways:

  • GET /gateway/api/v2/scanjobs/{scanJobId}/threats/pursuit/findings (List Pursuit findings from an investigation): findings from that job.
  • GET /gateway/api/v2/assets/{assetId}/threats/pursuit/findings: the latest findings for the asset. For a VM, this includes its attached volumes and disks.
  • GET /gateway/api/v1/assets/{asset_id}/pursuit-scans, then GET /gateway/api/v1/pursuit-scans/{pursuit_scan_id}: the investigation history, then one investigation with its playbook output (data.playbooks[], including text and run statistics).
GET
/gateway/api/v2/assets/{assetId}/threats/pursuit/findingsList the latest Pursuit findings for an asset
Try it
Terminal window
curl "https://acme.app.elastio.com/gateway/api/v2/assets/6f1c2a9e-3b4d-4e8f-9a7b-1c2d3e4f5a6b/threats/pursuit/findings?page_size=50" \
-H "Authorization: Bearer $ELASTIO_TOKEN"
{
"total": 1,
"data": [
{
"id": "c0d1e2f3-a4b5-4c6d-8e7f-9a0b1c2d3e4f",
"asset_id": "6f1c2a9e-3b4d-4e8f-9a7b-1c2d3e4f5a6b",
"pursuit_scan_id": "b7c6d5e4-f3a2-4b1c-9d0e-8f7a6b5c4d3e",
"title": "Scheduled task launches binary from user-writable path",
"category": "…",
"severity": "high",
"confidence": "medium",
"details": "…",
"persistence_type": "…",
"lol_bin": null,
"credential_assessment": null,
"evidence": [],
"extra": {},
"scanned_at": "2026-10-11T04:12:00Z"
}
],
"pagination": { "next_page_token": "", "prev_page_token": "" }
}

These endpoints only return findings from reports whose overall suspicion level is low or higher.

A Pursuit exception is one free-text instruction (up to 4096 characters) that Pursuit reads the next time it analyzes the asset. For example, it can explain that a tool is expected on that host. It is not a suppression. Each asset has at most one exception. Writing again edits it, or reinstates it after it has been cleared, and every change is audit-logged.

These endpoints are turned on per tenant. Where they aren’t enabled, they return 404.

PUT
/gateway/api/v1/assets/{asset_id}/pursuit-exceptionCreate, edit or reinstate an asset's Pursuit exception
Try it
Terminal window
curl -X PUT "https://acme.app.elastio.com/gateway/api/v1/assets/6f1c2a9e-3b4d-4e8f-9a7b-1c2d3e4f5a6b/pursuit-exception" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "instruction": "PsExec under C:\\Tools is installed by IT for patching and is expected on this host." }'
{
"asset_id": "6f1c2a9e-3b4d-4e8f-9a7b-1c2d3e4f5a6b",
"instruction": "PsExec under C:\\Tools is installed by IT for patching and is expected on this host.",
"status": "active",
"created_by": "…",
"created_at": "2026-10-12T09:40:00Z",
"updated_by": "…",
"updated_at": "2026-10-12T09:40:00Z",
"cleared_by": null,
"cleared_at": null
}

GET on the same path returns the active exception, or JSON null if there is none. DELETE clears it; clearing is idempotent and recorded in the history. GET …/pursuit-exception/history lists every change. To find assets that have an exception, use GET /gateway/api/v2/assets?has_pursuit_exception=true.