Privacy and data-subject requests

Export or erase a customer or partner, look a person up by email, and run access or erasure requests from a rights orchestrator.

Embed
Preview of the card
<a href="https://referalli.com/docs/privacy"><img src="https://referalli.com/docs/_api/public/orgs/07f432f4-34b9-46a7-aaad-c0e395d2e4f9/spaces/2bf4f614-3630-4fbf-85f9-5ecafa6b7d97/pages/17a0e998-31cd-4b09-bea6-904a32d8e007/social-card.svg?v=3e7ae33eec8b" alt="Privacy and data-subject requests" width="600" height="315"></a>

Referalli gives you the tools to answer data-subject requests (GDPR, CCPA and similar) for the data it holds on your behalf: customers you attributed, and partners in your program. Every call on this page needs the owner or admin role, or a full API key.

Erasure pseudonymizes. Identity is removed, but commission and payout amounts stay, because financial records have their own legal retention period. Balances and the ledger don't change.

Find someone by email

Customers are stored by email hash, not email, so you can't search them by address directly. lookup hashes the email for you (lowercase hex SHA-256 of the trimmed, lowercased address) and never stores it.

curl -X POST https://referalli.com/v1/privacy/lookup \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{"email": "dana@example.com"}'
# { "found": true, "customerRefs": ["cus_123"], "partnerId": "ptr_..." }

customerRefs lists every customer whose signup or identify call carried that email hash. partnerId is the partner in your org with that email, or null.

Export

curl "https://referalli.com/v1/privacy/export?customerRef=cus_123" \
  -H "Authorization: Bearer $KEY"

This returns every row that references the customer: referrals, associations, identityLinks, billingEvents and conversionEvents. To export a partner, or everything behind an email, open an access request.

Erase

POST /v1/privacy/forget takes exactly one of these:

Body

Erases

Response

{ "customerRef": "cus_123" }

One customer

{ "ok": true, "pseudonym": "forgotten_…" }

{ "partnerId": "ptr_..." }

One partner

{ "ok": true, "partners": [{ "partnerId", "alreadyForgotten" }] }

{ "email": "dana@example.com" }

The partner with that email and every customer stored under its hash

{ "ok": true, "partners": [...], "customers": [{ "customerRef", "pseudonym" }] }

If nothing matches a customerRef or email, the call fails with 400. An unknown partnerId returns 404.

Erasing a customer replaces the customer id with a forgotten_… pseudonym on referrals, associations, identity links, billing events, referee rewards, agent recommendations and consent records. It also:

  • clears the email hash, IP hash, user agent and anonymous id on their associations and identity links;

  • clears the IP hash and user agent on the visits and clicks of the browsers they were seen on (the clicks stay, as the partner's click history);

  • clears the provider metadata on their billing events, which can carry names or emails (amounts, dates and ids stay);

  • clears the email hash and declared data on their consent record.

Erasing a partner does the following:

  • Replaces the email with a forgotten-…@redacted.invalid address and the display name with "Erased partner", and clears notes and tags.

  • Clears the payout details and tax form data.

  • Renames their link and coupon codes, so they stop attributing (/go/{token}/{code} returns 404).

  • Deletes their postback.

  • Clears the free text they wrote in disputes and deal notes. A deal's company and contact are the prospect's business details and stay.

  • Payout invoices hold no partner identity (only numbers, dates and amounts; names are read live from the pseudonymised partner), so they need no change.

  • Unlinks their partner account, so they lose portal access to your program. Their account with other merchants is untouched.

  • Sets their status to banned, so nothing new is attributed or paid to the pseudonym.

The partner's commission lines, balances and payout history keep their amounts. Erasing a partner twice is safe: the second call returns alreadyForgotten: true.

Every erasure writes an audit log entry (privacy.forgotten or privacy.partner_forgotten).

Data-subject requests

Rights orchestrators work by opening a request and polling it. Referalli supports that pattern, so an orchestrator can register it as a connected system.

curl -X POST https://referalli.com/v1/privacy/requests \
  -H "Authorization: Bearer $KEY" \
  -H "content-type: application/json" \
  -d '{"subject": {"email": "dana@example.com"}, "type": "access"}'
# 202 { "id": "prq_01JB...", "status": "completed" }
  • type is access or erasure.

  • subject takes exactly one of email, customerRef or partnerId. An email resolves exactly like lookup. The request stores only the email's hash, as subject.emailHash.

  • Requests are processed straight away, so status is usually already completed. Poll anyway; the contract allows a request to start as pending.

curl https://referalli.com/v1/privacy/requests/prq_01JB... \
  -H "Authorization: Bearer $KEY"
{
  "id": "prq_01JB...",
  "type": "erasure",
  "subject": { "emailHash": "b4c9a2..." },
  "status": "completed",
  "error": null,
  "createdAt": "2026-10-21T12:00:00.000Z",
  "completedAt": "2026-10-21T12:00:00.000Z",
  "resultUrl": "/v1/privacy/requests/prq_01JB.../result",
  "result": { "found": true, "partners": [...], "customers": [...] }
}

Field

Meaning

status

pending, completed or failed. A failed request has its reason in error.

result

Included here for erasure requests: the same partners and customers receipts as forget.

resultUrl

GET it for the full result. For an access request that's the export: customers (one export per customer ref, as above) and partner (partner row, enrollments, links, coupons, payout profile, commission events and payouts).

When nobody matches the subject, the request still completes, with result.found: false. That's the answer an orchestrator needs; it isn't an error.

GET /v1/privacy/requests lists requests, newest first and paginated (the latest 200 under 2026-10-21), without their results.

Each finished request emits the privacy.request_completed webhook with requestId, type, status and, when completed, found.

Data retention

Referalli deletes raw data once it is older than your retention settings. A nightly job does it; each org is purged at most once a day. The commission ledger (earned, approved, paid, reversed and adjusted lines) is never purged.

Setting

What is purged

Default

Minimum

clicks

Raw clicks. Counts are kept as daily totals, so reports don't change, and a click a referral points at is kept.

forever

400 days

visits

Anonymous visit records.

400 days

30 days

billingPayloads

The raw provider payload on billing events (amounts, ids and dates stay).

forever

90 days

webhookDeliveries

Delivered or failed outbound webhook attempts. Pending ones are never purged.

30 days

7 days

notifications

Dashboard and portal notifications.

180 days

30 days

auditLog

Audit log entries.

forever

365 days

curl -X PUT $BASE/v1/org/retention \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{"clicks": 730, "auditLog": 2555}'

null keeps data forever; a value below the minimum returns 400. GET /v1/org/retention returns the policy, each kind's minimum and default, and when the purge last ran for your org. Owners and admins can change it, and every change is audited.

Updated