# Zero content retention

A team or profile setting that makes Zernio store identifiers and operational metadata only, never the content of messages, comments, leads, mentions, posts or calls.

Zero content retention is a setting for your whole team or for specific profiles. When it is on, Zernio stores identifiers and operational metadata only.

It covers inbox messages, comments, leads, mentions, publishing, calls, uploads and webhook logs.

## Turn it on

Turn it on for the whole team, or only for the profiles that need it, for example the clients of an agency that asked for it. When the team is on, it covers every profile.

The team owner turns it on or off in [Settings](https://zernio.com/dashboard/settings), in the **Content retention** section.

### For the whole team

Use `PUT /v1/content-retention`:

```bash
curl -X PUT https://zernio.com/api/v1/content-retention \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "none" }'
```

Send `{ "mode": "standard" }` to turn it off. `GET /v1/content-retention` returns the team setting and the profiles that have it on by themselves:

```json
{ "mode": "standard", "profileIds": ["64e1f0a9e2b5af0012ab34d1"] }
```

### For a profile

Set `contentRetention` on [update profile](/profiles/update-profile):

```bash
curl -X PUT https://zernio.com/api/v1/profiles/64e1f0a9e2b5af0012ab34d1 \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contentRetention": "none" }'
```

Send `"standard"` to turn it off for that profile. To store nothing from the first sync, create the profile with `"contentRetention": "none"` on [create profile](/profiles/create-profile), then connect its accounts.

Profiles return `contentRetention`, their own setting, and `effectiveContentRetention`, the mode that applies to them: `none` when the profile or the whole team has it on.

### Who can change it

- Only the owner can change it, at either level, signed in or with a full-access API key. Anyone else gets a `403`, and so do read-only, profile-scoped and restricted keys and third-party OAuth tokens.
- Turning it on answers `409` while a Zernio phone number of the team (or of that profile) has a WhatsApp verification in progress. Finish or cancel the verification first.
- Turning it off for one profile answers `409` while the whole team has it on. Turn it off for the team first.

## When it takes effect

The setting takes effect within about a minute and covers new data only.

- An account follows its profile. An account you connect to a profile that has it on is covered from the start, and an account you move to another profile follows the new one from then on.
- A post published to accounts of several profiles is treated as zero retention if any of those profiles has it on.

- Content stored before you turned it on stays until it is deleted.
- Messages already queued at that moment can still log their payload for up to about 51 hours.
- Turning it off makes new data store content again. Content that was not stored cannot come back.

## What Zernio stores

Zernio keeps identifiers and operational metadata:

- Conversation, message and account ids.
- Phone numbers and other platform identifiers. Zernio needs phone numbers to send and to match replies.
- Timestamps, delivery status, reactions and unread state.
- Contact data your team created through the API, a CSV import or the dashboard.
- For email, the sender address, kept as the identifier.

## What Zernio does not store

- Message text, sender names and profile pictures, and attachments.
- Comment text, lead answers and mention text.
- Workflow variables, once the run finishes.
- Contact data Zernio would otherwise capture from messages or mailbox sync.
- Email attachments. Zernio fetches them live from the email provider instead.

Nothing is kept for metrics. Data that security and platform operation depend on stays.

One exception: outgoing SMS text is kept for 30 days for abuse control. It is stored apart from your messages, out of the API, the dashboard, search and webhooks. Incoming SMS text is not stored.

## Webhooks are your only copy

Content reaches you once, in the webhook that announces it. Store it when it arrives.

When only some profiles have it on, this applies to their events. One endpoint can carry both kinds:

| Content | Event |
| --- | --- |
| Inbox messages | [`message.received`](/webhooks/inbox/message-received), [`message.sent`](/webhooks/inbox/message-sent) |
| Comments | [`comment.received`](/webhooks/inbox/comment-received) |
| Leads | [`lead.received`](/webhooks/ads/lead-received) |
| Mentions | [`mention.received`](/webhooks/inbox/mention-received) |
| Call transcripts | [`call.transcript_segment`](/webhooks/phone/call-transcript-segment) |
| Call recordings | [`call.recording_ready`](/webhooks/phone/call-recording-ready) |

- Lifecycle events (delivered, read, failed, deleted, edited) carry no content.
- Each event gets the full [retry schedule](/webhooks#retry-schedule) of about 51 hours. If your endpoint stays down longer, that event is lost. There is no buffer.
- Zernio never disables a failing endpoint of a team that has it on, for the team or for any profile. When an event is lost, Zernio emails the owner, at most once a week per endpoint. Monitor your endpoint yourself as well.
- `mention.received` is delivered at most once. Reconcile with `GET /v1/inbox/mentions`.
- Re-run in webhook logs is not available for these events and answers `422`.
- Media links in webhooks and Telegram channel avatars expire after about 3 days, and voice notes staged for transcoding after about 1 day. Download what you need.

## What the API and dashboard return

- Content fields come back as `null` with `contentNotRetained: true`. Ids and metadata stay.
- Contact, broadcast recipient and sequence enrollment names come back as `null` with no flag, because a contact your team created keeps its name.
- Search matches identifiers only. Conversation search says so in `meta.contentNotRetained`, and lead search in `contentNotRetained` in its meta.
- In webhook logs, `errorMessage` holds only `Detail not retained (<category>)`, and `requestPayload` and `responseBody` are empty. `status` and `statusCode` stay.
- `GET /v1/whatsapp/flow-responses` lists submissions without their answers (`data: null`).
- YouTube analytics return a video's `title` only on the first lookup of that video, and `null` after it.

Some reads go to the platform every time:

- `GET /v1/ads/lead-forms/{formId}/leads` reads live from Meta on every call, with no cache.
- `GET /v1/accounts/{accountId}/youtube-captions` downloads the transcript from YouTube on every call, with no cache, so each call spends YouTube quota.
- Comments and reviews skip Zernio's short cache too, so tight polling counts against your platform limits.

The Inbox shows conversations and their metadata with a "Content not retained" state. Sending and replying work as usual.

## Features that change

- AI workflow nodes that use history run with no conversation history.
- HubSpot timeline entries carry only their heading. A contact without a name or email is not created in HubSpot.
- A lost lead cannot be recovered once Meta no longer returns it. The undelivered lead sweep fetches leads again from Meta for 48 hours.

## Publishing

A post keeps its text only until it finishes publishing, about 10 minutes after the last platform ends. Scheduled posts and drafts keep their text until then.

After that, `content` and `title` come back as `null` with `contentNotRetained: true`. Ids, statuses, errors, media links and platform post ids stay. Pinterest posts keep their destination link, which the abuse scan needs.

- A failed post cannot be retried or edited (`422`). Create a new post instead.
- Recycling is not available.
- Search and tag filters do not match finished posts.
- Editing a published post on the platform still works. Zernio does not store the new text.
- Synced posts from your accounts store no caption, and `post.external.updated` does not fire for a caption-only edit.

## Scheduled emails

An email sent with `scheduledFor` keeps its content only until it is sent, fails or is cancelled. After that, Zernio keeps the recipient addresses, `metadata` and the tracking options.

- `subject` comes back as `null` with `contentNotRetained: true`.
- The subject, body, attachments, headers, sender name and recipient names are dropped, and so are the `cc`, `bcc` and `replyTo` addresses.
- With link tracking on, Zernio keeps the link URLs, so a click can still reach its destination.

## Uploaded media

Files uploaded with `POST /v1/media` or a presigned upload are single use. Zernio deletes one as soon as a post (a draft included) or a sent email uses it, and after 1 day if nothing does.

- Upload the file again for each post or email, even when it is the same file. A second post with the same URL returns `400`.
- A scheduled email fetches its attachment when it is sent, so an upload only works for a send within 1 day. For a later send, attach the file as `base64`.

An upload names no profile, so this applies to every upload of a team where any profile has it on. A post of a profile that keeps content still copies its media to permanent storage.

A post keeps its own copy of the media until it can publish. Then Zernio deletes it:

| Post | Media deleted |
| --- | --- |
| Published | 1 day after publishing |
| Failed | After 3 days |
| Draft | After 30 days without activity |
| Cancelled post or thread | Deleted too |

## Calls

Zernio stores no transcripts or recordings. `greeting` and `voicemailDropMessage` are kept only until the call ends.

- Transcripts arrive live as `call.transcript_segment`, one event per final segment.
- Recordings arrive as `call.recording_ready`, with a link that expires after about 3 days.
- Neither can be fetched later. With recording or transcription on and no webhook listening, you still pay and receive nothing.
- If Zernio cannot copy a recording, it is lost, and `callErrors` says so.

`GET /v1/calls/{id}/recording` answers `{ "url": null, "contentNotRetained": true }` in its JSON form, and `404` in its redirect form.

- The dashboard shows no player and no transcript.
- Voicemail emails carry the numbers and the time only.
- A call to a number with no voice routing hangs up.
- WhatsApp verification of a Zernio number is not available, and buying a number with WhatsApp answers `422`. Buy it without WhatsApp, or connect your own number.

## Providers outside this commitment

The commitment covers what Zernio stores. Telnyx carries calls and SMS and may keep copies for its own security, even though Zernio deletes each recording from Telnyx.

Outgoing SMS text also reaches Zernio's abuse scanning provider. Both providers are outside this commitment.

## If it fails

| Error | Cause | Fix |
| --- | --- | --- |
| `403` on `PUT /v1/content-retention` or on `contentRetention` of a profile | The caller is not the team owner, or the credential is not a full-access key or a session. | Use the owner's account or a full-access key. |
| `409` on `PUT /v1/content-retention` or on `contentRetention: "none"` of a profile | A Zernio number of the team or the profile has a WhatsApp verification in progress. | Finish or cancel the verification, then try again. |
| `409` on `contentRetention: "standard"` of a profile | The whole team has it on, which covers every profile. | Turn it off for the team first. |
| `422` on Re-run in webhook logs | Payloads are not stored, so there is nothing to send again. | Store events when they arrive. |
| `422` when retrying or editing a failed post | The post text is gone. | Create a new post. |

## Related

- [Webhooks](/webhooks): endpoints, retries and signatures.
- [Inbox webhooks](/webhooks/inbox): message, comment and mention events.
- [Phone webhooks](/webhooks/phone): call events, transcripts and recordings.
- [List mentions](/mentions/list-inbox-mentions): reconcile mentions you may have missed.
- [Security overview](/security): sign-in, roles and the audit log.