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.
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:- Begin — the subject submits email + phone + request type. An email OTP is sent immediately.
- Verify email — the 6-digit email code (10-minute TTL, 3 attempts; a 60-second resend cooldown).
- Send phone code — an SMS OTP to the same phone (60-second cooldown between sends).
- Verify phone — the 6-digit SMS code.
- Submit — only with both proofs on file does a request enter your queue. The whole claim has a 30-minute end-to-end TTL.
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.
Limit the queue to the jurisdictions whose rules you follow
The jurisdiction a request lands under is the single input that decides both the SLA clock it gets and whichrequest_type values it may
carry. Keeping the pairing right is worth the few minutes it takes to
learn — the DSAR reference documents the
applicable_jurisdiction enum and defaults; this section folds those
semantics into the runbook you run from.
- The operator default is
gdpr. Create a request without anapplicable_jurisdictionand it lands on the GDPR clock (30 days) with the GDPR right in scope. That is correct for EU/EEA subjects and wrong for anyone whose rights come from CCPA/CPRA. - Two rights exist only under CCPA/CPRA. A request of type
opt_out_sale(opt out of sale/share) orlimit_sensitive_pi(limit use of sensitive PI) has no GDPR equivalent, so the API rejects agdpr-scoped request of either type with422 VALIDATION_ERROR. Setapplicable_jurisdictionexpressly toccpaorcpraon those, never let it take the default. - Portal verbs are shorthand for the operator enum. A subject taps
one of four friendly labels; your queue runs on the operator’s
full vocabulary.
accessfiles asknow(the CCPA name for the GDPR access right),opt_outfiles asopt_out_sale, anddelete/portabilityarrive under their own names. Mapping a row back the other way for legal review is a lookup, not a triage.
cpra jurisdiction anchors a 45-day clock and admits the
opt_out_sale type; leave jurisdiction out and the same body fails at
the door with 422 VALIDATION_ERROR. The same pairing discipline
applies to limit_sensitive_pi — and when a subject files the shorter
opt_out through the portal, the intake path defaults jurisdiction to
CCPA/CPRA for you (above), precisely because the GDPR default would
otherwise be invalid.
Five operator mistakes that burn your SLA clock
Each of these shows up in queue triage; each is a one-word fix if you know where the jurisdiction/request-type semantics live.-
Omitting
applicable_jurisdictionon a California consumer. The default isgdpr, which starts a 30-day clock. Passccpaorcprawhen the subject is a California consumer — and pass it on every operator-filed request, so the habit never relies on defaults. -
Reaching for a California-only right while
gdpris in scope.opt_out_saleandlimit_sensitive_piexist only under CCPA/CPRA; agdpr-scoped request of either type is rejected with422 VALIDATION_ERROR. File the request underccpaorcpra, set the right, and the error goes away. -
Misreading the portal’s shorthand verbs. A subject who taps
accesshas filedknow(the CCPA name for the access right);opt_outhas filedopt_out_sale. When a legal team asks you to report on the right in question, map the portal verb back to the operator enum before you answer — do not paraphrase. -
Filing the short name instead of the operator enum. The API
enum is the operator’s vocabulary (
know,opt_out_sale,limit_sensitive_pi, …). Sendrequest_type: "opt_out"from your own integration and validation rejects it — the shorthand belongs to the portal surface only. -
Trusting the default for the CPRA opt-out. A CPRA opt-out of
sale/share filed with no jurisdiction is a GDPR-scoped
opt_out_sale— rejected at the door. The explicit pair is one line:
Lifecycle visibility for ops
Portal filings that match a contact in your tenant land in Settings → Compliance → DSAR, in the same list and the sameGET /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: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.
Troubleshooting and related links
- Troubleshooting: DSAR export, decrypt, and cancel failures — when a fulfilled export will not download, decrypt, or cancel.
- Fulfill a DSAR and operate the breach-incident register — the operator walkthrough of the queue your portal feeds.
- Public consent form — a sibling hosted public form, for opt-in/opt-out rather than rights requests.
- DSAR reference — the full endpoint surface, abuse-defence limits, and SLA severity tiers.