Skip to content
Try the API

Recovery verdicts and evidence

Auditors and cyber insurers increasingly ask for proof that you can recover, not just that you have backups. This guide shows how to pull Elastio’s recovery verdict for the whole estate, list the assets that aren’t proven recoverable and why, and export the evidence behind each verdict.

Each live asset is checked against an effective target: a recovery RPO in seconds (r_rpo_seconds) and any required copies (immutable, air_gapped, cross_region). The target comes from one of four sources, recorded in effective_target.source: program_class_floor, program_node, policy or default.

  • proven: there’s a recent enough clean recovery point that has the required copies. qualifying_point names it.
  • unproven: no such point exists. unproven_reasons explains why, for example no_recovery_point, no_clean_point, r_rpo_exceeded, missing_immutable_copy or air_gapped_copy_not_observable.

A proven asset can also be at risk (at_risk: true), meaning it’s proven today but likely to lapse. The reasons are no_enabled_policy, plan_not_activated, near_expiry and policy_looser_than_target. It can also be proven with exceptions, where a commitment has been relaxed by an approved, time-limited governance override listed in exceptions.

GET
/gateway/shared-services/v1/recovery-verdictEstate-wide recovery verdict summary
Try it

This is the headline figure: counts.proven_count out of estate_total live assets, from the last evaluation (evaluated_at). You get it whether or not you have committed a Resilience Program. Assets excluded from the program are still counted here, measured against their policy or default target. An asset that hasn’t been evaluated yet counts as unproven, with no reason.

Terminal window
curl "https://acme.app.elastio.com/gateway/shared-services/v1/recovery-verdict" \
-H "Authorization: Bearer $ELASTIO_TOKEN"

Example response (trimmed):

{
"verdict_model": "recovery_verdict_v1",
"evaluated_at": "2026-10-12T11:00:00Z",
"program_committed": true,
"estate_total": 555,
"counts": {
"proven_count": 489,
"unproven_count": 66,
"at_risk_count": 21,
"proven_with_exceptions_count": 8,
"unproven_by_reason": { "r_rpo_exceeded": 31, "no_clean_point": 4, "missing_immutable_copy": 22, "no_recovery_point": 12 },
"at_risk_by_reason": { "near_expiry": 14, "no_enabled_policy": 7 }
},
"by_class": {
"critical": {
"asset_total": 60,
"proven_count": 56,
"unproven_count": 4,
"at_risk_count": 2,
"proven_with_exceptions_count": 1,
"unproven_by_reason": { "r_rpo_exceeded": 4 },
"at_risk_by_reason": { "near_expiry": 2 }
},
"important": { … },
"standard": { … },
"excluded": { … }
},
"by_target_source": { "program_class_floor": 300, "program_node": 140, "policy": 80, "default": 35 }
}

An asset with several unproven reasons counts under each of them, so the reason counts can add up to more than unproven_count.

GET
/gateway/shared-services/v1/recovery-verdict/assetsPer-asset recovery verdicts, paged
Try it

The per-asset list gives you a work list, or the appendix for an audit. Filters are combined with AND:

Parameter Values
scope estate (default, every live asset) or program (the program’s promised assets)
verdict proven, unproven
reason One unproven or at-risk reason code
class critical, important, standard, excluded
with_exceptions true for assets proven with exceptions
page, page_size 1-based page number. Page size defaults to 500, which is also the maximum.

Rows are ordered by asset name. total is the count before paging, so with scope=program, the totals match the snapshot’s totals.proven_count and totals.unproven_count. An asset that hasn’t been evaluated yet has recovery: null.

Terminal window
curl "https://acme.app.elastio.com/gateway/shared-services/v1/recovery-verdict/assets?verdict=unproven&class=critical&page=1&page_size=500" \
-H "Authorization: Bearer $ELASTIO_TOKEN"

Example response (trimmed):

{
"assets": [
{
"asset_id": "2b7e9c1d-3f4a-4b5c-8d6e-7f8091a2b3c4",
"name": "prod-db-01",
"kind": "…",
"class": "critical",
"account_id": "123456789012",
"region": "us-east-1",
"recovery": {
"verdict": "unproven",
"unproven_reasons": ["r_rpo_exceeded"],
"at_risk": false,
"at_risk_reasons": [],
"proven_with_exceptions": false,
"exceptions": [],
"effective_target": { "r_rpo_seconds": 86400, "required_copies": ["immutable"], "source": "program_class_floor" },
"newest_clean_point_at": "2026-10-09T02:04:00Z",
"qualifying_point": null,
"compromise_started_at": null,
"evaluated_at": "2026-10-12T11:00:00Z"
}
}
],
"page": 1,
"page_size": 500,
"total": 4
}
GET
/gateway/shared-services/v1/assets/{asset_id}/evidenceGet the aggregated evidence envelope for one asset
Try it

The evidence envelope collects everything Elastio knows about one asset’s recoverability in a single document:

  • determination: the latest hunt result (clean, infected or not_scanned) and when it ran.
  • recovery_posture: the last clean recovery point, backup age against the RPO target, and up to 200 recovery points from the last 365 days.
  • recovery_verdict: the same object as recovery in the per-asset list.
  • coverage: the full matrix of analyses that were run, including clean ones.
  • tested_backup: the latest backup, inventory, hunt, restore and verification results.
  • threats and violations: open findings on the asset and its attached volumes or disks.

If one section can’t be read, the call still returns 200 and names that section in degraded. Check degraded before you treat a missing section as “no evidence”. A 404 means the tenant has no such live asset.

Terminal window
curl "https://acme.app.elastio.com/gateway/shared-services/v1/assets/2b7e9c1d-3f4a-4b5c-8d6e-7f8091a2b3c4/evidence" \
-H "Authorization: Bearer $ELASTIO_TOKEN" \
-o prod-db-01-evidence.json

Example response (trimmed):

{
"asset": { "id": "2b7e9c1d-3f4a-4b5c-8d6e-7f8091a2b3c4", "name": "prod-db-01", "kind": "…", "platform": "aws", "health": "…" },
"determination": { "state": "clean", "as_of": "2026-10-12T02:04:00Z", "elapsed_seconds": 34560 },
"recovery_posture": {
"last_clean_rp": { "timestamp": "2026-10-12T02:04:00Z", "policy_names": ["prod-db-nightly"], "backup_vendor": null },
"objectives": { "backup_age_seconds": 34560, "rpo_target_seconds": 86400 },
"recovery_points": [
{ "id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d", "when": "2026-10-12T02:04:00Z", "status": "…", "is_last_clean": true }
],
"coverage_sources": ["…"],
"efs_backup_association": null
},
"recovery_verdict": {
"verdict": "proven",
"unproven_reasons": [],
"at_risk": false,
"at_risk_reasons": [],
"proven_with_exceptions": false,
"exceptions": [],
"effective_target": { "r_rpo_seconds": 86400, "required_copies": ["immutable"], "source": "program_class_floor" },
"qualifying_point": { "recovery_point_id": "5a4b3c2d-1e0f-4a9b-8c7d-6e5f4a3b2c1d", "created_at": "2026-10-12T02:04:00Z", "copies": ["immutable"] },
"evaluated_at": "2026-10-12T11:00:00Z"
},
"coverage": [
{
"method": "…",
"status": "ran",
"verdict": "clean",
"at": "2026-10-12T02:04:00Z",
"subject": { "id": "…", "kind": "…", "label": "…" },
"finding_id": null,
"freshness": null,
"score": null,
"note": null
}
],
"tested_backup": { "backup": { "at": "…", "result": "…" }, "iscan": { "at": "…", "result": "…" }, "restore": null, "inventory": null, "verification": null },
"threats": [],
"violations": { "by_severity": { "critical": 0, "high": 0, "medium": 0, "low": 0 }, "earliest_detected": null, "items": [] },
"degraded": []
}
GET
/gateway/shared-services/v1/evidence/ledgerGet one page of the fleet-wide evidence ledger
Try it

The ledger lists every analysis record across the fleet, one row per analysis, in the same shape as an asset’s coverage entries plus the asset. coverage and population summarise the whole scope, not just the current page: how many analyses ran, came back clean, raised findings, weren’t run, were inconclusive or errored, and how many assets were expected versus retrieved. Inconclusive and errored analyses are never counted in ran or expected. Page with page_size (default 500) and next_page_token.

Terminal window
curl "https://acme.app.elastio.com/gateway/shared-services/v1/evidence/ledger?page_size=500" \
-H "Authorization: Bearer $ELASTIO_TOKEN"

Example response (trimmed):

{
"scope": { "kind": "…", "label": "…", "params": {} },
"coverage": { "expected": 1660, "ran": 1602, "clean": 1571, "findings": 31, "not_run": 58, "inconclusive": 6, "errored": 2 },
"population": { "assets_expected": 555, "assets_attempted": 555, "assets_retrieved": 553, "assets_failed": 2, "cap": 0, "cap_applied": false },
"records": [
{
"asset": { "id": "2b7e9c1d-3f4a-4b5c-8d6e-7f8091a2b3c4", "name": "prod-db-01", "kind": "…", "platform": "aws", "health": "…" },
"method": "…",
"status": "ran",
"verdict": "clean",
"at": "2026-10-12T02:04:00Z",
"subject": { "id": "…", "kind": "…", "label": "…" },
"finding_id": null,
"freshness": null,
"score": null,
"note": null
}
],
"pagination": { "offset": 0, "page_size": 500, "total": 1660, "next_page_token": "…" },
"degraded": []
}