Skip to content
Try the API

Manage protection policies

Policies decide which assets Elastio protects, how often, and which hunts run on them. This guide shows how to manage policies as code: preview which assets a policy would select, create it on a shared schedule, check its membership, run it on demand, and retire it safely.

Policies are served in three versions. Use v3 (/gateway/shared-services/v3/policies) for new integrations. It covers AWS, Azure and IBM, and adds the per-policy violations selection and the r_rpo recovery RPO target on top of v2. The v1 and v2 policy endpoints still work, but v3 is a superset of both.

Policies are identified by name in the path, for example /v3/policies/{name}. URL-encode the name.

Need Endpoint family
Scheduled hunt or backup policies /gateway/shared-services/v3/policies
Event-triggered policies /gateway/shared-services/v3/event-policies
Shared cadences that several policies reference /gateway/shared-services/v1/schedules
Which assets a policy selects /gateway/shared-services/v2/policies/{name}/membership, /v2/policies/scope-preview, /v2/assets/{id}/policies
Routing of operational events and unprotected-asset findings /gateway/shared-services/v3/system-policies
GET
/gateway/shared-services/v3/policiesList tenant policies v3
Try it

The optional platform filter takes AWS, Azure, IBM or GCP. The response wraps the policies in policies and includes a total_count.

Terminal window
curl "https://acme.app.elastio.com/gateway/shared-services/v3/policies?platform=AWS" \
-H "Authorization: Bearer $ELASTIO_TOKEN"

Example response (trimmed):

{
"policies": [
{
"name": "prod-s3-scan",
"platform": "AWS",
"kind": "custom",
"status": "enabled",
"paused_until": null,
"schedule_id": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
"frequency": { "kind_tag": "daily", "monday": true, "tuesday": true, "wednesday": true, "thursday": true, "friday": true, "saturday": true, "sunday": true, "time": { "time": "02:00:00", "timezone": "Etc/UTC" } },
"selectors": [ { "criteria": [ { "kind_tag": "is_type", "asset_type": "s3" } ] } ],
"mode": { "kind_tag": "scan_only", "s3": { "threat_hunt": false } },
"iscan": { "ransomware": true, "malware": true, "encryption_detection": false, "data_classification": false, "hunt": false, "pursuit": false },
"r_rpo": { "target_seconds": 86400, "severity": "high" },
"violations": [],
"last_run_at": "2026-10-12T02:00:00Z",
…
}
],
"total_count": 1
}

Preview a policy’s scope before you save it

Section titled “Preview a policy’s scope before you save it”
POST
/gateway/shared-services/v2/policies/scope-previewCompute the assets a draft policy definition would select
Try it

Scope preview runs a draft definition against your current inventory and returns the asset IDs it would select. It saves nothing. AWS drafts send selectors and account_region_selectors in the same shape the policies API uses. Azure and IBM drafts send policy_yaml. The platform field is required. Results are paged with the limit (1–1000, default 100) and offset query parameters.

This draft selects every S3 bucket in one account and region, and skips anything tagged backup=skip. An exclude_tags criterion removes an asset if it matches any of the listed tags, and exclusions take precedence over the include criteria.

Terminal window
curl -X POST "https://acme.app.elastio.com/gateway/shared-services/v2/policies/scope-preview?limit=100" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "prod-s3-scan",
"platform": "AWS",
"account_region_selectors": [ { "account_id": "123456789012", "region": "us-east-1" } ],
"selectors": [
{ "criteria": [
{ "kind_tag": "is_type", "asset_type": "s3" },
{ "kind_tag": "exclude_tags", "tags": { "backup": "skip" } }
] }
]
}'

Example response:

{
"policy_name": "prod-s3-scan",
"platform": "AWS",
"candidate_assets": 212,
"total_matched": 3,
"asset_ids": [
"0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d5e",
"2c3d4e5f-6a7b-4c8d-8e9f-1a2b3c4d5e6f"
],
"computed_at": "2026-10-12T10:30:00Z",
"pagination": { "limit": 100, "offset": 0 }
}

Create a schedule and a policy that uses it

Section titled “Create a schedule and a policy that uses it”

A policy’s cadence comes from one of two places: an inline frequency, or a schedule_id that points to a shared schedule. Sending both is rejected. A shared schedule is useful when several policies should run at the same time. Changing the schedule’s frequency changes the cadence of every policy that references it straight away.

POST
/gateway/shared-services/v1/schedulesCreate schedule (Admin only)
Try it

name (unique within the tenant, 1–255 characters) and frequency are required. frequency.kind_tag is one of every5_minutes, every15_minutes, every30_minutes, every_hour, every12_hours, daily, weekly or monthly. A daily frequency needs all seven weekday booleans. A weekly frequency needs day, and a monthly frequency needs day_of_month. Times use {"time": "HH:MM:SS", "timezone": "<IANA zone>"}.

Terminal window
curl -X POST "https://acme.app.elastio.com/gateway/shared-services/v1/schedules" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "nightly-0200-utc",
"description": "Every night at 02:00 UTC",
"frequency": {
"kind_tag": "daily",
"monday": true, "tuesday": true, "wednesday": true, "thursday": true,
"friday": true, "saturday": true, "sunday": true,
"time": { "time": "02:00:00", "timezone": "Etc/UTC" }
}
}'

Example response (201 Created):

{
"id": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
"name": "nightly-0200-utc",
"description": "Every night at 02:00 UTC",
"frequency": {
"kind_tag": "daily",
"monday": true, "tuesday": true, "wednesday": true, "thursday": true,
"friday": true, "saturday": true, "sunday": true,
"time": { "time": "02:00:00", "timezone": "Etc/UTC" }
},
"tenant_name": "acme",
"created_at": "2026-10-12T10:35:00Z",
"updated_at": "2026-10-12T10:35:00Z"
}
POST
/gateway/shared-services/v3/policiesCreate policy v3 (Admin only)
Try it

For AWS, name and platform are required, and a usable policy also needs selectors and mode. This example creates a hunt-only policy (scan_only mode) for the S3 buckets previewed above, hunting for ransomware and malware on the shared schedule. It also sets a recovery RPO target: if the newest proven clean recovery point is older than 24 hours, Elastio raises a high severity finding.

mode.kind_tag is backup or scan_only. scan_only mode needs at least one family option bag: ec2_or_ebs, s3, efs or fsx_ontap. iscan needs all six booleans: ransomware, malware, encryption_detection, data_classification, hunt and pursuit.

Terminal window
curl -X POST "https://acme.app.elastio.com/gateway/shared-services/v3/policies" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "prod-s3-scan",
"platform": "AWS",
"description": "Nightly ransomware and malware hunt of production S3",
"schedule_id": "4c5d6e7f-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
"account_region_selectors": [ { "account_id": "123456789012", "region": "us-east-1" } ],
"selectors": [
{ "criteria": [
{ "kind_tag": "is_type", "asset_type": "s3" },
{ "kind_tag": "exclude_tags", "tags": { "backup": "skip" } }
] }
],
"mode": { "kind_tag": "scan_only", "s3": { "threat_hunt": false } },
"iscan": {
"ransomware": true, "malware": true, "encryption_detection": false,
"data_classification": false, "hunt": false, "pursuit": false
},
"r_rpo": { "target_seconds": 86400, "severity": "high" }
}'

The response (201 Created) is the stored policy, in the same shape as a row from the list call. Because the policy references a schedule, frequency is filled in from that schedule. violations, alert_ids, hunt_rule_ids and ccl_severity_overrides are always present and come back as [] when empty.

PUT /gateway/shared-services/v3/policies/{name} takes the same body as create, without name. platform is required and can’t be changed. A v3 update fully replaces violations and r_rpo: if you leave them out, they’re cleared. Read the policy first, change the fields you need, and send the whole definition back.

GET
/gateway/shared-services/v2/policies/{name}/membershipCompute the assets currently selected by a stored policy
Try it

By default (source=computed), the policy’s selectors are evaluated live against the current inventory. source=stored reads the membership saved by the last engine pass instead, and returns 503 where that storage isn’t enabled. For the reverse lookup, which policies cover one asset, use GET /gateway/shared-services/v2/assets/{id}/policies.

Terminal window
curl "https://acme.app.elastio.com/gateway/shared-services/v2/policies/prod-s3-scan/membership?limit=1000" \
-H "Authorization: Bearer $ELASTIO_TOKEN"

The response has the same shape as the scope preview: policy_name, platform, candidate_assets, total_matched, asset_ids, computed_at and pagination.

POST
/gateway/shared-services/v3/policies/{name}/runRun policy on demand (Admin only)
Try it

This starts an immediate run of a saved policy. It takes no body and doesn’t change the policy. It returns 202 Accepted with the parent job_id. Per-connector child jobs carry this ID as their parent, so you can follow the whole run from it (see Track jobs). Run is AWS only. Azure and IBM policies are delivered as cluster YAML and have no per-connector job.

Terminal window
curl -X POST "https://acme.app.elastio.com/gateway/shared-services/v3/policies/prod-s3-scan/run" \
-H "Authorization: Bearer $ELASTIO_TOKEN"

Example response (202 Accepted):

{ "job_id": "9f8e7d6c-5b4a-4392-8170-6f5e4d3c2b1a" }

POST /gateway/shared-services/v3/policies/{name}/pause requires a body. {"paused_until": "2026-10-20T00:00:00Z"} pauses the policy until that time (status becomes paused_until). An empty object {} disables it indefinitely (status becomes disabled). POST …/resume takes no body and turns the policy back on. Both return 204.

POST
/gateway/shared-services/v3/policies/{name}/soft-deleteSoft-delete a scheduled policy by name (Admin only)
Try it

Prefer soft delete. It saves a snapshot of the policy, removes it from the live list and stops it from running, but you can still restore it. The body is optional, {"reason": "…"} (up to 1024 characters). The response is the deletion record.

Terminal window
curl -X POST "https://acme.app.elastio.com/gateway/shared-services/v3/policies/prod-s3-scan/soft-delete" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "reason": "Replaced by prod-s3-scan-v2" }'

Example response (trimmed):

{
"id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
"kind": "scheduled",
"platform": "AWS",
"last_known_name": "prod-s3-scan",
"last_known_id": "c2d3e4f5-a6b7-4c8d-9e0f-1a2b3c4d5e6f",
"prior_state": "enabled",
"delete_reason": "Replaced by prod-s3-scan-v2",
"deleted_at": "2026-10-12T11:00:00Z",
"deleted_by": "admin@example.com",
"restored_at": null,
"hard_deleted_at": null,
"alert_ids": [],
"prior_snapshot": { … },
…
}

To find soft-deleted policies, call GET /gateway/shared-services/v3/policies/deleted. Restore one with POST /gateway/shared-services/v3/policies/deleted/{id}/restore, or delete it permanently with DELETE /gateway/shared-services/v3/policies/deleted/{id}.

PUT
/gateway/shared-services/v3/system-policies/{id}Update a system policy
Try it

System policies control alerting that isn’t tied to one asset policy. A system_events policy covers operational events: the kinds listed in event_kinds, or every kind if all_event_kinds is true. The unprotected_assets policy covers findings on assets that no enabled protection policy covers. Seeded policies exist for every tenant and can’t be deleted. A tenant that hasn’t moved to System Policies yet gets an empty list.

  1. List the tenant’s system policies with GET /gateway/shared-services/v3/system-policies and note the id of the one you want.
  2. List the available event kinds with GET /gateway/shared-services/v3/system-events/catalog. Kinds with available: false aren’t emitted yet.
  3. Update the policy. Fields you leave out keep their stored values. If you send alert_ids, it replaces the attached alerts and must contain at least one.
Terminal window
curl -X PUT "https://acme.app.elastio.com/gateway/shared-services/v3/system-policies/d3e4f5a6-b7c8-4d9e-8f0a-1b2c3d4e5f6a" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "enabled",
"all_event_kinds": true,
"alert_ids": [ "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b" ]
}'

Example response (trimmed):

{
"id": "d3e4f5a6-b7c8-4d9e-8f0a-1b2c3d4e5f6a",
"name": "…",
"description": "…",
"type": "system_events",
"status": "enabled",
"is_seeded": true,
"all_event_kinds": true,
"event_kinds": [],
"event_severities": [],
"scope": null,
"alert_ids": [ "e4f5a6b7-c8d9-4e0f-9a1b-2c3d4e5f6a7b" ],
"created_at": "2026-01-15T09:00:00Z",
"updated_at": "2026-10-12T11:10:00Z"
}

You can also limit a system policy to certain accounts or tags with scope. Set scope to null to apply it tenant-wide. To change the severity of individual event kinds, send event_severities as { "event_kind", "severity_override" } pairs. The API has no endpoint for creating new system policies.

Policies

36 operations in the reference →

Policy Schedules

5 operations in the reference →

Policy Membership

5 operations in the reference →

System Policies

4 operations in the reference →

System Events

3 operations in the reference →