Skip to main content

Data Subject Access Requests (DSAR)

A Data Subject Access Request (also called a privacy request or consumer rights request) is the formal mechanism a person uses to exercise their rights over the personal data you hold about them — the right to access, delete, correct, port, or opt out of the sale of that data. Most privacy laws give you a hard deadline to respond (30 days under GDPR, 45 under CCPA/CPRA). Orbit gives you two intake paths and one fulfilment pipeline:
  • Operator-filed DSAR — your support or compliance team files a request on a customer’s behalf through the authenticated API or dashboard.
  • Public self-service portal — the data subject files their own request through a public, unauthenticated flow that proves their identity with a two-factor email + SMS OTP before anything is queued.
This page describes Orbit’s platform controls. It is not legal advice. Your obligations — which laws apply, what you must disclose, and how long you have — depend on where your data subjects live and what data you process. Confirm with qualified counsel.
All endpoints below are rooted at https://api.orbit.devotel.io/api/v1/compliance.

Supported jurisdictions and deadlines

The applicable_jurisdiction on a request controls which statutory clock Orbit’s SLA tracker applies. Operators can reclassify a request after intake.

Request types

request_type describes what the subject is asking for. The full CCPA/CPRA verb set is available to operators; the public portal exposes a friendlier subset that maps onto it. For CCPA access requests you may also attach consumer_categories — the CCPA §1798.100(b) categories the subject is asking about: identifiers, customer_records, protected_classifications, commercial, biometric, internet_activity, geolocation, sensory, professional, education, inferences, sensitive_pi.

Operator-filed requests

Create a request

POST /compliance/dsar — requires an admin or owner API key. Provide at least one subject identifier (contact_id, subject_email, or subject_phone) plus the requester_email that should receive correspondence.
Returns 202 Accepted:
Noteapplicable_jurisdiction only defaults to gdpr for rights that exist under GDPR. The opt_out_sale and limit_sensitive_pi request types are CCPA/CPRA-only and have no GDPR equivalent, so you must set applicable_jurisdiction to ccpa or cpra explicitly for them. Omitting it (or leaving the gdpr default) is rejected with 422 VALIDATION_ERROR.

Status lifecycle

A request moves through: receivedprocessingcompleted with terminal branches failed, expired, and cancelled. The verification sub-state is tracked independently: pendingverified (worker proceeds) or rejected (worker halts). GDPR/admin-filed rows default to not_required.

Verify or reject identity

Higher-assurance requests (delete, opt-out, limit-sensitive) require an operator decision before fulfilment proceeds:
decision is verified or rejected; notes is optional (≤ 2048 chars). Returns the new verification_status and verified_at.

Cancel a request

POST /compliance/dsar/{id}/cancel withdraws an in-flight request (GDPR Art 7(3)). Only works while the request is received or processing; a terminal request returns 409 Conflict.

List and read requests

  • GET /compliance/dsar — paginated list. Query: page (≥ 1), page_size (≤ 100, default 25), and an optional status filter.
  • GET /compliance/dsar/{id} — fetch one request. The response includes the signed export_url (and its export_expires_at) once an access/portability export has been produced, plus tables_exported describing the per-table row counts.

Erasure requests

GDPR Art 17 erasures are tracked as their own resource so you can audit and intervene before data is destroyed:
  • GET /compliance/dsar/erasure-requests — list. Query: status (pending, cancelled, executing, executed, failed) and limit (≤ 500).
  • POST /compliance/dsar/erasure-requests/{id}/cancel — cancel a pending erasure before it executes. Optional reason (≤ 500 chars). Returns 409 if it is already executing or done.

SLA dashboard

GET /compliance/dsar/sla returns a combined export + erasure SLA snapshot so you never miss a statutory deadline:
Severity tiers scale proportionally with each jurisdiction’s SLA window — the day thresholds are anchored to the GDPR 30-day case and multiplied by the slaDays / 30 ratio, so a request always turns amber and red at the same fraction of its own deadline. escalation_due flips 5 days before the statutory deadline (slaDays − 5). For GDPR (sla_days: 30): green (< 20 days elapsed), amber (20–25), red (26–30), red + breach (> 30); escalation_due at day 25. For CCPA/CPRA (sla_days: 45) the same ratios give green (< 30), amber (30–38), red (39–45), red + breach (> 45); escalation_due at day 40. Always read the tier boundaries against the sla_days returned for that request, not the fixed 20/25/30 numbers.

Public self-service portal

The public flow lets a data subject file a request without an account. Identity is proven with a two-factor OTP — an email code and an SMS code — before any request is queued. The endpoints live under /compliance/public/dsar and are unauthenticated, but are defended by Cloudflare Turnstile, per-IP and per-identifier rate limits, and a privacy-preserving response shape that never reveals whether an email/phone pair matches a real contact.
SMS verification codes are delivered through the Devotel softswitch (the platform’s sole outbound SMS path). They are platform OTPs, not tenant-billable traffic, and carry no delivery-receipt persistence.

Flow overview

1

Begin

POST /compliance/public/dsar/begin with email, phone (E.164), request_type (access | delete | portability | opt_out), and a Cloudflare turnstile_token (required in production). Returns an opaque claim_id, email_sent: true, and expires_in: 600. An email OTP is sent immediately.
2

Verify email

POST /compliance/public/dsar/verify-email with claim_id and the 6-digit code. Returns state email_verified and next step phone_send. Codes expire after 10 minutes; max 3 attempts. POST …/resend-email (with claim_id + email) issues a new code, subject to a 60-second cooldown.
3

Send phone code

POST /compliance/public/dsar/send-phone with claim_id and the phone that matches the one given at begin. Sends an SMS OTP (expires_in: 600). A 60-second cooldown applies between sends; a too-soon retry returns 429 with Retry-After.
4

Verify phone

POST /compliance/public/dsar/verify-phone with claim_id and the 6-digit code. Returns state phone_verified and next step submit.
5

Submit

POST /compliance/public/dsar/submit with claim_id. Persists an audit row and — only if the verified email + phone match a contact in your tenant — queues a real DSAR (pre-marked verification_status: verified, since the OTP already proved identity). Returns a reference_id (e.g. dsar_pub_…) and a queued boolean.

Configure the identity-proof senders

The two OTPs are sent from platform-level senders you configure once in your API environment. Set them before publishing the portal — an unset SMS sender with no fallback makes the phone step fail closed. If DEVOTEL_DSAR_PROOF_SMS_FROM and DEVOTEL_PLATFORM_DEFAULT_FROM are both unset, the send-phone step fails closed with a 503 — the portal returns a “temporarily unavailable” message and the failure is emitted under the dsar.proof.sms_send_failed metric so it surfaces in your dashboards rather than silently skipping the second factor. Likewise, the email step returns 503 when DEVOTEL_RESEND_API_KEY is unset. Configure both senders before you link the portal publicly.

Abuse defenses

The response shape is identical whether or not the identifiers match a real contact — the portal never confirms or denies that someone is in your database. When Redis is unavailable the rate-limit gates fail open to preserve availability.

Enabling Turnstile protection

The Turnstile gate is configured with two environment variables.
The gate is fail-open when DEVOTEL_TURNSTILE_SECRET_KEY is unset: begin accepts requests without a token and logs a single warning. Set the secret in production, or the portal is unprotected by Turnstile even though every other abuse defense above still applies. Generate both keys in the Cloudflare dashboard (Turnstile → Add site) and set them on the API and web deployments respectively.

Publish the public portal under your privacy policy as the “Submit a privacy request” link. Because the flow self-verifies via OTP, requests that arrive through it are already identity-proven — they land in your operator queue ready to fulfil, and appear in GET /compliance/dsar alongside operator-filed requests.