Advanced
Retention audits
File a data-retention audit report after each cleanup run.
If your organization runs its own data-retention cleanup against its own database, it must file an audit report after each cleanup run by calling this endpoint. Reports are stored immutably under a prefix derived from your organization id.
Endpoint#
POST https://core-api.heysadie.ai/v1/retention/audits
Content-Type: application/json
Authorization: ApiKey YOUR_ORG_API_KEYThis is a /v1 path
Unlike the rest of the API, the audit endpoints live under /v1. Authenticate with your organization-level API key — this endpoint is org-scoped, so no X-Tenant-Id header is required. Tenant-level keys are rejected with 401. See Organization-level access.
Payload#
The body is any JSON object you like — it is stored exactly as you send it. Your cleanup process is yours; report it in whatever shape it produces. Only three rules:
| Rule | Detail |
|---|---|
runId (required) | UUID, unique per cleanup run. It names the stored file — re-sending the same runId overwrites the same file, so retries are idempotent |
finishedAt (recommended) | ISO 8601. Anchors the year/month folder your audit is filed under; if omitted, the time of receipt is used |
| Size | Max 10MB per payload |
source is not part of the payload — it is derived server-side from your authenticated organization id. You cannot write into another organization's audit prefix (any source you send is overwritten).
For reference, this is a reasonable template if you're starting from scratch:
{
"runId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"retentionDays": 90,
"cutoffDate": "2026-03-13T00:00:00.000Z",
"startedAt": "2026-06-11T03:00:00.000Z",
"finishedAt": "2026-06-11T03:01:30.000Z",
"status": "completed",
"callsScrubbed": 19080,
"batchesExecuted": 39,
"oldestCallDate": "2025-07-31T09:02:45.000Z",
"newestCallDate": "2026-03-13T18:31:28.000Z",
"scrubbedFields": ["transcript", "recording_url", "summary", "analysis", "messages"],
"callIds": ["<scrubbed-call-id>", "..."]
}Responses#
A successful submission returns the standard envelope with { "stored": true, "runId": "..." }.
| Status | Meaning | What to do |
|---|---|---|
| 200 | Audit stored | Mark the run as reported |
| 400 | Missing/invalid runId, payload over 10MB, or body is not a JSON object | Fix the payload; do not retry as-is |
| 401 / 403 | Bad or missing key | Check your org API key |
| 503 | Storage temporarily unavailable (RETENTION_AUDIT_STORAGE_FAILED) | Keep the run marked unreported; re-send on your next cleanup run |
Expected cadence#
Run your cleanup nightly (off-peak) and report each run right after it finishes. If a report fails, re-send it on your next run — runId idempotency makes this safe. OnCore alerts when an organization goes quiet for several days, so consistent reporting avoids false alarms.
Churned-customer cleanups
Cleaning up a churned customer's data (as opposed to the ongoing retention window) is reported to a separate endpoint — see Churn audits.