SOC 2 / HIPAA evidence pack
The evidence endpoint snapshots a workspace’s live state — compliance mode + retention, members per role, custom roles, IP allowlist status, SSO / SCIM bindings, API key count, and audit-log volume over the trailing 30 / 90 days. It’s the artefact your auditor asks for when they say “show me on date X”, and the source-of-truth answer to ~80% of vendor security questionnaires.
The endpoint is read-only and gated on the
compliance.viewpermission, so a “compliance officer” custom role can produce evidence without holding any other workspace mutations.
What you get
Two surfaces — same snapshot, different shape:
| Method | Path | Shape |
|---|---|---|
GET | /v1/workspaces/{ws}/compliance/evidence | JSON |
GET | /v1/workspaces/{ws}/compliance/evidence.zip | ZIP archive |
The JSON is the structured snapshot for tools (Vanta / Drata / your
internal evidence tracker). The ZIP is what you hand a human
auditor — same JSON inside, plus the trailing 90 days of audit
events as CSV, current membership as CSV, the IP allowlist as CSV,
and a README.md mapping each file to the SOC 2 control it covers.
JSON snapshot
curl 'https://api.elido.app/v1/workspaces/1/compliance/evidence' \
-H "Authorization: Bearer $ELIDO_TOKEN"{
"workspace_id": 1,
"collected_at": "2026-05-01T12:00:00Z",
"controls_matrix_url": "https://docs.elido.app/compliance/SOC2",
"posture": {
"mode": "soc2",
"audit_retention_days": 2190,
"default_audit_retention_days": 730,
"ip_allowlist_enabled": true,
"ip_rules_count": 4
},
"access": {
"members_total": 18,
"members_by_role": { "owner": 1, "admin": 3, "editor": 9, "viewer": 5 },
"custom_roles_count": 2,
"pending_invitations": 0,
"api_keys_active": 6,
"api_keys_last_created": "2026-04-12T08:30:00Z"
},
"auditing": {
"events_last_30_days": 4812,
"events_last_90_days": 14201,
"oldest_retained": "2024-04-30T00:00:00Z",
"newest_event": "2026-05-01T11:58:43Z"
},
"integrations": {
"sso_bound": true,
"scim_bound": true,
"scim_enabled": true,
"baa_signed_count": 1
}
}ZIP bundle
curl -OJ 'https://api.elido.app/v1/workspaces/1/compliance/evidence.zip' \
-H "Authorization: Bearer $ELIDO_TOKEN"Bundle layout:
elido-evidence-1-20260501.zip
├── evidence.json (the snapshot above)
├── audit-events-90d.csv (trailing 90d audit log)
├── members.csv (current workspace membership)
├── ip-rules.csv (current IP allowlist)
└── README.md (file → SOC 2 control mapping)audit-events-90d.csv is hard-capped at 90 days because evidence
packs are reviewed point-in-time; longer trails should come from
your SIEM webhook firehose (webhooks guide)
which streams every event in near-real-time to your own SIEM.
Mapping to SOC 2 controls
The bundle’s README.md maps each file to the AICPA Trust Services
Criteria your auditor will tick off. Summary:
| File | Maps to |
|---|---|
evidence.json (whole snapshot) | CC1.5 (accountability), CC6.1 (logical access), CC7.1 (monitoring) |
audit-events-90d.csv | CC1.5 (accountability evidence), CC4.1 (monitoring), CC7.1 (event detection) |
members.csv | CC6.1 (restrict access), CC6.3 (authorise users), CC6.5 (deprovision) |
ip-rules.csv | CC6.6 (logical access — protect external) |
The full control-matrix narrative lives at
docs/compliance/SOC2 —
the controls_matrix_url field in the snapshot points there.
Self-hosting note
The endpoint is part of services/api-core and runs on the same
Postgres pool as the rest of the API — no extra moving parts. The
trailing-90d audit-log stream is bounded by your workspace’s
audit_retention_days; a HIPAA workspace at 2190d retention can
produce a 6-year history if you ever need it (export the bundle
multiple times across the window).
SDK
import { ElidoApi } from "@elido/sdk";
const api = new ElidoApi({ baseUrl, token });
const snap = await api.compliance.evidence(workspaceId);
console.log(snap.access.members_by_role);
// For the ZIP, hand the URL to a download mechanism — fetch + stream
// to disk, or `<a href download>` in a browser.
window.location.href = api.compliance.evidenceBundleUrl(workspaceId);import "github.com/elidoapp/elido/packages/sdk-go/elido"
c, _ := elido.New(elido.Options{BaseURL: baseURL, Token: token})
snap, _ := c.Compliance.Evidence(ctx, workspaceID)
fmt.Println(snap.Posture.Mode)
// ZIP URL — caller streams the body with its own auth header.
zipURL := c.Compliance.EvidenceBundleURL(workspaceID)import elido
client = elido.Client(base_url=base_url, token=token)
snap = client.compliance.evidence(workspace_id)
print(snap["access"]["members_by_role"])
zip_url = client.compliance.evidence_bundle_url(workspace_id)Endpoint reference
| Method | Path |
|---|---|
GET | /v1/workspaces/{ws}/compliance/evidence |
GET | /v1/workspaces/{ws}/compliance/evidence.zip |