> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# CDP retention windows: per-data-class TTL governance

> Configure how long Orbit retains each CDP data class — raw track events, destination delivery log, and tracking-plan violations — with per-event overrides on the raw stream, guard rails burned into product, and full rollback to platform defaults.

# CDP retention windows

GDPR storage limitation (Article 5(1)(e)) asks you to keep personal data no
longer than needed — and most privacy programs answer that with a written
policy saying "we retain analytics events for N days." Orbit's CDP makes that
policy something you can **enforce in-product**: the **Retention** tab on the
CDP page (`/integrations/cdp`) exposes a per-data-class time-to-live window
for every prune-able CDP surface, and the API contract behind it —
`GET/PUT/DELETE /api/v1/cdp/retention-policy` — lets you script the same
posture. A tenant with no overrides behaves exactly as the platform ships;
nothing changes until you set a window.

## Section 1 — What data classes retention governs

Three prune-able classes, each with a documented platform default:

| Data class               | Key                        | What it holds                                                                                                                                   | Default |
| ------------------------ | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| Raw track events         | `raw_track_events`         | The analytics event stream written by the Segment-compatible `track` call — the source the event debugger, funnels, and segment recompute read. | 90 days |
| Destination delivery log | `deliveries`               | One row per outbound destination delivery (webhook, CRM sync, warehouse export) — the SLA/reliability query surface.                            | 90 days |
| Tracking-plan violations | `tracking_plan_violations` | Events rejected by your [tracking plan](/guides/cdp-tracking-plan) — schema mismatches, unplanned event names.                                  | 30 days |

Profiles, identifier graph entries, and computed traits are **not** retention
targets — they persist until a contact erasure removes them. Retention
governs the rolling event/delivery/violation stores; identity is governed by
[erasure propagation](/guides/cdp-erasure-propagation).

## Section 2 — Available windows and guard rails

Each class carries a `[min_days, max_days]` range burned into the product.
The server clamps any override into that range, so a tenant can never set an
unsafe window at either end:

* **Minimum ≥ 1 day** — a 0-day "delete on write" policy would race the
  ingest path and destroy in-flight analytics. The floor makes that
  impossible.
* **Maximum per class** — raw events cap at **730 days**, deliveries and
  violations at **365 days**. The cap keeps the hot-table index and SLA-query
  assumptions each surface was sized for intact.

The catalog endpoint returns each class's live range; the dashboard dialog
renders the range inline ("Allowed range 1–730 days") and re-checks the value
client-side before it enables save.

| Class key                  | Allowed range | Per-event overrides |
| -------------------------- | ------------- | ------------------- |
| `raw_track_events`         | 1–730 days    | Supported           |
| `deliveries`               | 1–365 days    | Not supported       |
| `tracking_plan_violations` | 1–365 days    | Not supported       |

## Section 3 — What is preserved vs deleted per class

The reaper prunes a class on the class window, and the resolution order
decides which window applies to any given row:

1. A **per-event-name override** (raw track events only) — exact match on the
   event name.
2. The **class-level override**, if you set one.
3. The **platform default**, otherwise.

Deleted: any row in the class older than its resolved window. Preserved:
everything younger, plus all non-retention data (profiles, identifier graph,
accounts, segments) — a tight retention window never collapses a profile;
erasure does that.

## Section 4 — API contract

All routes are owner/admin/developer + `contacts:read` for reads and
`contacts:write` for writes.

```http theme={null}
GET    /api/v1/cdp/retention-policy           # every class: effective window, override registry, catalog, summary
GET    /api/v1/cdp/retention-policy/:target   # one class; add ?event_name= to resolve a per-event window
PUT    /api/v1/cdp/retention-policy/:target   # set/replace the class override (+ per-event map, + note)
DELETE /api/v1/cdp/retention-policy/:target   # remove the override — class reverts to platform default
```

Each response includes `source` — `default`, `target_override`, or
`event_override` — so an auditor can see exactly which layer resolved the
window in force today. Response envelope per class:

```json theme={null}
{
  "target": "raw_track_events",
  "retention_days": 45,
  "source": "target_override",
  "default_days": 90,
  "event_name": null
}
```

## Section 5 — Preview before save

Don't PUT blind. The single-target GET returns the class's effective window
**before** you write, with the guard-rail spec attached — build the row the
operator sees from that response:

```bash theme={null}
curl "https://api.orbit.devotel.io/api/v1/cdp/retention-policy/raw_track_events" \
  -H "Authorization: Bearer $API_KEY"
```

Response:

```json theme={null}
{
  "data": {
    "target": "raw_track_events",
    "retention_days": 90,
    "source": "default",
    "default_days": 90,
    "spec": { "min_days": 1, "max_days": 730, "supports_event_overrides": true },
    "override": null
  },
  "meta": { "request_id": "req_x", "timestamp": "2026-09-16T00:00:00.000Z" }
}
```

The dashboard dialog shows the same preview ("Platform default is 90 days");
out-of-range values are clamped on save, never silently truncated.

## Section 6 — Sample PUT: several classes in one policy

Send **one PUT per class** — the registry is per-key. Here raw events tighten
to 45 days with two per-event carve-outs, deliveries stay extended to a year,
and violations tighten to 14 days:

```bash theme={null}
# 1. Tighten the raw event window, keep "Order Completed" longer, purge "Page Viewed" fast
curl -X PUT "https://api.orbit.devotel.io/api/v1/cdp/retention-policy/raw_track_events" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{
    "retention_days": 45,
    "event_overrides": { "Order Completed": 365, "Page Viewed": 14 },
    "notes": "GDPR Art.5(1)(e) storage-limitation posture 2026-Q3"
  }'

# 2. Extend the delivery log for reliability analytics
curl -X PUT "https://api.orbit.devotel.io/api/v1/cdp/retention-policy/deliveries" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "retention_days": 180, "notes": "Quarterly SLA review window" }'

# 3. Tighten violations visibility
curl -X PUT "https://api.orbit.devotel.io/api/v1/cdp/retention-policy/tracking_plan_violations" \
  -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
  -d '{ "retention_days": 14 }'
```

`event_overrides` is rejected with `EVENT_OVERRIDES_UNSUPPORTED` (400) on a
class that doesn't support it — only `raw_track_events` accepts the map. The
`notes` field (max 500 chars) rides along as audit context in the registry.

## Section 7 — Rollback semantics

Removing an override is **DELETE /retention-policy/:target** — the class
reverts to the platform default on the next reaper pass, and the DELETE
response returns the now-effective window (`source: "default"`). The
guard-rail clamp means an accidentally-requested `99999` becomes
`max_days`, and the accidental `0` becomes `min_days` — never the dangerous
extreme either direction. Retained data is not affected retroactively when
you reset; rollback only changes the boundary the next prune uses.

## Section 8 — Tenant-safety warning

Retention is **tenant-scoped**: the registry lives on your organization's own
settings row, so one tenant can never see or shorten another tenant's
window, and there is no global gate. The warning is about blast radius
inside your tenant: a shortened window is how **downstream erasure and
clean-room requests propagate the same way** — when an Article-17 erasure
runs, destinations replay against the current effective envelope, so a
tightened retention policy shrinks what an erasure can fan out to (nothing
outside the window exists to propagate). Review retention changes alongside
your [erasure-propagation policy](/guides/cdp-erasure-propagation) before
tightening a window for an active DSAR posture, and treat retention edits
the same way compliance treats erasure-policy edits: an audited, reasoned
change.

## Related

* [CDP event model](/concepts/cdp-event-model) — what a track event is
* [CDP tracking plan](/guides/cdp-tracking-plan) — violations class
* [Erasure propagation](/guides/cdp-erasure-propagation) — delete fan-out
* [API: CDP endpoints](/api-reference/endpoints/cdp)
