Skip to main content
Causeloop provides a data governance layer for operators that need to comply with GDPR and similar privacy regulations. The API exposes endpoints for retention management, GDPR Article 17 erasure requests (RTBF), and tenant data export. Services for executing these operations live in app/services/.

Data governance API

All governance endpoints require the governance:write or governance:read scope. Only users with the admin role have these scopes by default. Base path: /v1/governance/
EndpointMethodScopeDescription
/governance/exportsPOSTgovernance:writeRequest a full export of workspace data
/governance/exportsGETgovernance:readList export requests
/governance/exports/{id}GETgovernance:readGet export status and download link
/governance/rtbf/requestsPOSTgovernance:writeSubmit an Article 17 RTBF request
/governance/rtbf/requestsGETgovernance:readList RTBF requests
/governance/rtbf/requests/{id}GETgovernance:readGet RTBF request status
/governance/rtbf/requests/{id}/approvePOSTgovernance:writeApprove and execute erasure
/governance/rtbf/requests/{id}/rejectPOSTgovernance:writeReject the request with a reason
/governance/rtbf/requests/{id}/certificateGETgovernance:readDownload the erasure certificate (completed requests only)

Retention policies

Retention is configured per-workspace in workspace_settings. Each class of data has its own retention window:
Setting keyApplies toDefault
issue_retention_daysissues tablePer workspace config
activity_retention_daysactivity_events tablePer workspace config
prediction_retention_dayspredictions tablePer workspace config
audit_log_retention_daysaudit_log tableCompliance floor: ≥ 365 days

How retention enforcement works

app/services/retention.py provides purge_expired(database_url), which:
  1. Fetches all workspace IDs
  2. For each workspace, reads the retention windows from workspace_settings
  3. Sets app.current_workspace GUC (so RLS applies)
  4. Deletes rows older than the configured window from each table
# Tables and their retention setting keys
RETENTION_MAP = {
    "issues":           "issue_retention_days",
    "activity_events":  "activity_retention_days",
    "predictions":      "prediction_retention_days",
    "audit_log":        "audit_log_retention_days",
}
The purge_expired() function is implemented but not yet scheduled. It must be called by an external cron job or scheduled worker. A periodic task that calls purge_expired(DATABASE_URL) on a daily schedule is the recommended integration. Automated scheduling is on the SOC 2 roadmap — see SOC 2 readiness.
Running retention manually:
# From the application environment with DATABASE_URL set
python3 -c "
from app.services.retention import purge_expired
import os
result = purge_expired(os.environ['DATABASE_URL'])
print(result)  # {'issues': N, 'activity_events': N, ...}
"
Audit floor: the audit log has a compliance minimum of 365 days. If audit_log_retention_days is set below this, the purge skips audit log rows to prevent premature deletion of evidence.

Right-to-be-forgotten (RTBF — GDPR Article 17)

Causeloop implements a multi-step RTBF workflow with state tracking and an erasure certificate.

RTBF states

received → verifying → approved → processing → completed
                    ↘ rejected
                    ↘ on_hold

Submitting a request

curl -X POST https://api.causeloop.ai/v1/governance/rtbf/requests \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "mode": "erasure",
    "reason": "GDPR Article 17 request received via email"
  }'
The request subject’s email is immediately HMAC-SHA256-hashed into a privacy-preserved subject_ref. The plaintext email is only used to identify the subject during processing and is not retained beyond the request record. Duplicate guard: if an active RTBF request already exists for the same subject in the same workspace, the API returns 409 Conflict. SLA: the due_at field is set to 30 days from received_at, corresponding to GDPR’s response deadline.

Approving and executing erasure

curl -X POST https://api.causeloop.ai/v1/governance/rtbf/requests/{id}/approve \
  -H "Authorization: Bearer <admin-token>"
On approval, the application calls erase_workspace() from app/services/rtbf.py, which:
  1. Resolves the workspace’s organization slug
  2. Executes hard deletes of the subject’s data across tenant tables
  3. Records a persistent erasure record
erase_workspace() requires DATABASE_URL to be available at runtime. When the application is running against the in-memory store (no DATABASE_URL), the approve endpoint records the approval transition but cannot execute the physical deletion. In production with Postgres, the deletion executes synchronously on approval.

Erasure certificate

Once a request reaches the completed state, the erasure certificate is available:
curl https://api.causeloop.ai/v1/governance/rtbf/requests/{id}/certificate \
  -H "Authorization: Bearer <admin-token>"
# → {"url": "...", "issued_at": "2026-06-14T...", "request_id": "..."}

Tenant data export

The export service (app/services/tenant_export.py) assembles a complete snapshot of a workspace’s business data. It runs RLS-scoped so it can only access the requesting workspace’s rows.

What is exported

TableColumns exported
issuesid, title, body, status, severity, created_at
patternsid, name, status, risk_score, created_at
predictionsid, pattern_id, probability, created_at
recommendationsid, title, status, created_at
connectorsid, type, display_name, status, created_at
audit_logid, action, actor_id, target_type, target_id, created_at, trace_id
Intentionally excluded: config_encrypted (connector credentials), secret_encrypted (webhook secrets), and all other secret columns. These are never included in exports.

Requesting an export

curl -X POST https://api.causeloop.ai/v1/gdpr/export-requests \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{"format": "json"}'
The current export implementation returns data inline in the response body. Export delivery to object storage (S3/GCS presigned URLs) with checksums is planned. See SOC 2 readiness for the current status.

Data residency

Causeloop does not enforce data residency at the application layer — residency is determined by where you run the database. For EU data residency:
  • Use Neon’s EU region (Frankfurt) when creating your Neon project
  • Deploy the API to an EU region (Hetzner Falkenstein/Helsinki, Railway EU, Render Frankfurt)
  • Configure your LLM provider’s EU endpoint if available, or use the mock provider
If using Anthropic or OpenAI for AI features, data sent to these providers is subject to their privacy policies and data processing agreements. You are responsible for executing DPAs with sub-processors before processing personal data through them.

Offboarding and full tenant deletion

To permanently delete all data for a tenant (not just an individual), use offboard_client():
SELECT offboard_client('acme', 'purge');
This cascades through the entire tenant data tree. See Client provisioning — offboarding for details.

GDPR compliance summary

ArticleCoverageStatus
Art. 5 — Purpose limitation & minimisationDefined retention windows; secrets excluded from exportsPartial — purge job not yet scheduled
Art. 17 — Right to erasureRTBF API with state tracking, duplicate guard, erasure certificatePartial — deletion pipeline requires Postgres at runtime
Art. 20 — Data portabilityTenant export APIPartial — inline response; object storage delivery planned
Art. 30 — Records of processingAudit log, governance endpointsIn place
Art. 32 — Technical measuresEnvelope encryption, RLS, TLS, RBACPartial — see SOC 2 gaps