Skip to main content

Deploy the public self-service DSAR intake portal

Devotel Orbit ships a public, unauthenticated intake page where a data subject files a privacy request themselves — access, erasure, portability, or opt-out — and proves their identity with a two-factor email + SMS OTP before anything enters your queue. This guide walks through deciding on, publishing, and operating that portal. For the endpoint surface and jurisdiction tables, read the DSAR reference; for the operator-side walkthrough of the queue portal filings land in, read the DSAR operator guide.
The portal is your intake surface. Orbit hosts it and tracks the statutory clocks, but the legal duty to respond within the deadline stays with you, the controller. Confirm your obligations with counsel.

When the public portal beats operator-filed DSAR

The operator flow (file a request in the console) works perfectly well — until it does not:
  • Volume. Every intake routed through your support or compliance team costs an operator round-trip per request. High-volume consumer businesses get subject requests daily; self-service takes them to zero ops touches.
  • Unauthenticated subjects. An operator-filed request via your authenticated API can only be filed by someone with a session. The public portal gives strangers on the internet a lawful front door.
  • Statutory clocks. GDPR Article 12 gives you 30 days, CCPA/CPRA 45, LGPD 15. An intake channel that forces every request through a human queue burns those days on triage. Portal filings arrive pre-verified, so the clock is spent on fulfilment, not identity paperwork.
If you publish a privacy policy that promises a self-serve channel — and most GDPR-obligated controllers do — the portal is how that promise actually works.

Deploy the portal

Route and hosting model

The portal is a hosted public page on Orbit’s web surface — no account, no API key, no session. Publish it as the “Submit a privacy request” link in your privacy policy, website footer, and in-product settings:
<locale> follows the same locale prefix as every other Orbit web page (for example en, fr, de). The page renders a two-step identifier form behind a Cloudflare Turnstile bot check: the subject enters their email and phone (E.164), chooses a request type, and the identity proof begins.

The two-factor identity proof

Nothing is queued until the subject proves they control both an email address and a phone number:
  1. Begin — the subject submits email + phone + request type. An email OTP is sent immediately.
  2. Verify email — the 6-digit email code (10-minute TTL, 3 attempts; a 60-second resend cooldown).
  3. Send phone code — an SMS OTP to the same phone (60-second cooldown between sends).
  4. Verify phone — the 6-digit SMS code.
  5. Submit — only with both proofs on file does a request enter your queue. The whole claim has a 30-minute end-to-end TTL.
The identity proof runs against platform-level senders you configure in your API environment — an unset SMS sender (with the platform fallback unset too) fails the phone step closed with a 503, and the email step fails closed when the email delivery key is missing. Configure both before you link the portal publicly: In production, the first step also requires a Turnstile token: DEVOTEL_TURNSTILE_SECRET_KEY on the API and NEXT_PUBLIC_DEVOTEL_TURNSTILE_SITE_KEY on the web deployment. Both keys come from the Cloudflare dashboard (Turnstile → Add site). Without the secret the gate is open — set it in production. SMS verification codes go out through the Devotel softswitch as platform OTPs — they are not tenant-billable traffic.

Request types a subject can pick

The public form exposes a friendlier subset that maps onto the operator enum: Operator-only rights (correct, limit-sensitive-PI, non-discrimination) are intentionally not exposed publicly — a subject who needs those can still file through your operators.

Jurisdiction and the SLA clock

The subject picks the jurisdiction their request falls under; that choice sets the statutory clock your queue displays: Operators can reclassify the jurisdiction after intake — so a subject’s mis-selection is fixable inside your queue, not a wrong clock you must live with. When a portal filing targets opt_out, jurisdiction defaults to CCPA/CPRA rather than GDPR, matching the operator-side validation that rejects a GDPR-scoped opt-out request.

Lifecycle visibility for ops

Portal filings that match a contact in your tenant land in Settings → Compliance → DSAR, in the same list and the same GET /compliance/dsar response as operator-filed requests. Because the OTP already proved identity, they arrive pre-marked verification_status: verified — the export or erasure worker starts immediately and no operator approve/reject gate sits in the way.
  • Erasure-class portal filings (the subject picked “delete”) are pulled into the Erasure requests (Art. 17) tab’s portal section — the same place the console groups erasures from the admin dialog — with their SLA badge beside them.
  • Wrong jurisdiction picked? Reclassify the row from the queue; the SLA badge re-anchors to the corrected window.
  • Need the paper trail? The document-verification and manual-review methods exist for operator-filed rows; a portal row’s proof is its two-factor OTP, recorded in the audit chain (below).

Evidence a portal filing leaves behind

Every stage of the public flow writes to your tamper-evident audit log — the begin attempt, the email verification, the phone verification, and the submit — with hashed (never raw) identifier values, the source IP, and the user agent. That is the record a regulator or your evidence binder pulls from when they ask how a subject’s identity was established. Two consequences to plan around:
  • The breach/incident register and binder count audit rows across both intake paths — portal filings do not need a separate evidential story.
  • When a filing does not match any contact, an audit row is still written (scoped to the platform rather than your tenant) so the forensic trail exists even for a no-match case — while the portal’s response shape still never reveals whether a contact matched.

Integrate the API directly

The portal page is a thin client over a public REST sequence. If you embed your own intake UI (an in-app privacy center, a partner-hosted form), call the same handlers — the response envelope and rate limits are documented in the DSAR reference:
Only the submit step returns 201; every earlier step is a 2xx progression toward it. Whatever the integration, render a Turnstile widget at step 1 — production begin calls without a token are rejected with 422 TURNSTILE_REQUIRED.