Data retention and deletion requests¶
Two complementary mechanisms for keeping AiFlow's stored call and message data bounded: an ongoing, automatic age-based purge configured by whoever operates your deployment, and a single on-demand endpoint for a specific "right to be forgotten" request tied to one person's phone number, email address, or Telegram chat.
On-demand deletion: DELETE /api/v1/data-subjects¶
Purges every Call (and its full transcript) and every OutboundMessage
tied to a given phone number, email address, or Telegram chat id.
Authentication and roles¶
Takes an admin session token (see Authentication), not an API key. Requires Owner. This is the only endpoint on this site's admin surface reserved to Owner specifically, rather than Owner-or-Admin. It's the single most destructive, compliance-sensitive action in this API: it deletes real customer records with no undo.
Query parameters¶
| Parameter | Type | Required | Description |
|---|---|---|---|
phone |
string | At least one of the three | Match on this exact phone number. |
email |
string | At least one of the three | Match on this exact email address. |
telegram_chat_id |
string | At least one of the three | Match on this exact Telegram chat id. |
Any combination can be supplied together, and records matching any of them
are purged. If none is supplied, this returns 400 Bad Request with
{"detail": "Provide at least one of phone, email, or telegram_chat_id."}.
A Telegram send is addressed to a numeric chat id rather than a phone
number or an email, so it is only reached by telegram_chat_id. Include it
whenever a request covers someone your agents messaged on Telegram. No
Call is ever addressed to a chat, so a telegram_chat_id on its own
deletes messages and leaves calls alone.
Returns 200 OK:
| Field | Type | Description |
|---|---|---|
calls_deleted |
integer | Number of Call rows deleted. |
outbound_messages_deleted |
integer | Number of OutboundMessage rows deleted (WhatsApp, Telegram, and email sends all live in this same table). |
Exactly what this deletes¶
- Calls: every
Callrow wherephone_numberequals the givenphone, orvisitor_emailequals the givenemail(a call matches on either condition if both query parameters were supplied). Deleting aCallalso deletes everyTurnrow belonging to it, its full transcript. There is no way to delete a call's turns independently, or to keep the transcript after the call record is gone. - Outbound messages: every
OutboundMessagerow whoseto_addressequals any value given.to_addressis a single shared column holding a phone number for a WhatsApp send, an email address for an email send, and a chat id for a Telegram send, so all three parameters check the same column, just against different values. - An audit log entry is written for the deletion
(
action: "delete",resource_type: "data_subject",resource_id: nullsince the action spans a set of records rather than one).detailcontains thephoneoremailyou passed, plus the two counts above.
What this does not delete¶
Event payloads are not scanned or purged
This endpoint never touches the Event table. An event's payload
is arbitrary JSON your own systems posted (see
Sending events), with no schema AiFlow controls
or can reliably interpret. It isn't scanned for a matching phone
number or email address, even if the data you're trying to purge
originally arrived inside one. The Event row that originally
caused a now-deleted Call to be queued is untouched by this
endpoint: it's governed only by the time-based
EVENT_LOG_RETENTION_DAYS setting below, not by identity. If your
compliance requirements cover inbound event payloads specifically,
scrub personal data out of them at your own source system before
posting to AiFlow. This API has no mechanism to do that for you
after the fact.
Ongoing retention: environment variables, or live from the dashboard¶
Independent of the on-demand endpoint above, whoever operates your
AiFlow deployment can configure automatic, age-based purging so records
don't accumulate forever by default. Each setting below starts from an
environment variable. A live override saved via GET or
PATCH /api/v1/settings/operational (or Settings → Retry &
retention, Owner or Admin only) takes over from it afterward, with no
restart needed. GET always reflects the effective value, whichever
one is actually in force:
| Setting | Env var | Type | Default | Effect |
|---|---|---|---|---|
call_transcript_retention_days |
CALL_TRANSCRIPT_RETENTION_DAYS |
integer | unset (keep forever) | Automatically deletes Call records (and their Turn transcripts) older than this many days. |
event_log_retention_days |
EVENT_LOG_RETENTION_DAYS |
integer | unset (keep forever) | Automatically deletes Event records older than this many days. |
outbound_message_retention_days |
OUTBOUND_MESSAGE_RETENTION_DAYS |
integer | unset (keep forever) | Automatically deletes OutboundMessage records older than this many days. |
Each is independent: a deployment can set any combination of the three,
or none of them. Leaving one unset (on the environment-variable side) or
null (on the override side) keeps that record type indefinitely. There
is no single master switch: each retention window is controlled
independently. This is the mechanism that ages out an Event row over
time, even though the on-demand endpoint above cannot target
one by phone or email. Purging happens on an hourly tick, not the
instant a record crosses the cutoff. A saved override applies starting
from the next tick.
PATCH /api/v1/settings/operational also covers a fourth,
non-retention setting, outbound_call_max_retries, in the same request:
the endpoint always replaces all four values together, matching the
dashboard's single combined form, not a per-field partial update.
curl -X PATCH https://api.your-domain.com/api/v1/settings/operational \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-d '{
"outbound_call_max_retries": 3,
"call_transcript_retention_days": 365,
"event_log_retention_days": 90,
"outbound_message_retention_days": 90
}'
{
"outbound_call_max_retries": 3,
"call_transcript_retention_days": 365,
"event_log_retention_days": 90,
"outbound_message_retention_days": 90
}
Which one to use¶
Use the on-demand endpoint when someone has made a specific deletion request: you know their phone number or email and need their records gone now, on your own schedule, independent of any configured retention window. Use the retention-window settings above for the ongoing baseline: data you don't have a standing reason to keep past a certain age, purged automatically without anyone having to remember to call this endpoint.
Next¶
- Audit log: confirm a deletion request was actually carried out, and by whom.
- Sending events: where
Event.payload(out of scope for the on-demand endpoint above) originates.