> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Demandes d'accès des personnes concernées (DSAR)

> Recevez, vérifiez et traitez les demandes de personnes concernées RGPD, CCPA, CPRA, LGPD, PDPA et DPDP sur Orbit via les flux de l'opérateur ou le portail en libre-service.

# Demandes d'accès des personnes concernées (DSAR)

Une **demande d'accès des personnes concernées** (aussi appelée demande de
vie privée ou demande de droits des consommateurs) est le mécanisme formel
par lequel une personne exerce ses droits sur les données personnelles que
vous détenez à son sujet — le droit à l'**accès**, à la **suppression**, à
la **rectification**, à la **portabilité**, ou à l'**opposition à la
vente** de ces données. La plupart des lois sur la vie privée vous imposent
un délai strict de réponse (30 jours sous le RGPD, 45 sous CCPA/CPRA).

Orbit vous offre deux chemins de réception et un pipeline de traitement :

* **DSAR déposée par un opérateur** — votre équipe support ou conformité
  dépose une demande pour le compte d'un client via l'API authentifiée ou
  le tableau de bord.
* **Portail public en libre-service** — la personne concernée dépose sa
  propre demande via un flux public non authentifié qui prouve son identité
  par un **OTP à deux facteurs e-mail + SMS** avant que quoi que ce soit
  soit mis en file.

<Warning>
  Cette page décrit les contrôles de plateforme d'Orbit. **Ce n'est pas un
  avis juridique.** Vos obligations — quelles lois s'appliquent, ce que vous
  devez divulguer, et dans quel délai — dépendent du lieu de résidence de
  vos personnes concernées et des données que vous traitez. Confirmez
  auprès d'un conseil juridique qualifié.
</Warning>

Tous les endpoints ci-dessous sont racinés à
`https://api.orbit.devotel.io/api/v1/compliance`.

***

## Juridictions prises en charge et délais

Le `applicable_jurisdiction` d'une demande détermine quelle horloge légale
le suivi des SLA d'Orbit applique. Les opérateurs peuvent reclasser une
demande après sa réception.

| Juridiction                | Code     | SLA de réponse |
| -------------------------- | -------- | -------------- |
| RGPD UE / EEE              | `gdpr`   | 30 jours       |
| CCPA Californie            | `ccpa`   | 45 jours       |
| CPRA Californie            | `cpra`   | 45 jours       |
| LGPD Brésil                | `lgpd`   | 15 jours       |
| PDPA Singapour / Thaïlande | `pdpa`   | 30 jours       |
| PIPEDA Canada              | `pipeda` | 30 jours       |
| DPDP Inde                  | `dpdp`   | 30 jours       |

## Types de demandes

`request_type` décrit ce que la personne demande. L'ensemble complet des
verbes CCPA/CPRA est disponible pour les opérateurs ; le portail public
expose un sous-ensemble plus convivial qui s'y rattache.

| `request_type` opérateur | Signification                                                        | Verbe du portail public |
| ------------------------ | -------------------------------------------------------------------- | ----------------------- |
| `know`                   | Accès — divulguer les données détenues (RGPD Art 15, CCPA §1798.110) | `access`                |
| `delete`                 | Effacement (RGPD Art 17, CCPA §1798.105)                             | `delete`                |
| `correct`                | Rectification (RGPD Art 16, CPRA §1798.106)                          | —                       |
| `portability`            | Export lisible par machine (RGPD Art 20)                             | `portability`           |
| `opt_out_sale`           | Opposition à la vente/partage (CCPA §1798.120)                       | `opt_out`               |
| `limit_sensitive_pi`     | Limiter l'usage des PI sensibles (CPRA §1798.121)                    | —                       |
| `non_discrimination`     | Droit de non-discrimination (CCPA §1798.125)                         | —                       |

Pour les demandes d'accès CCPA, vous pouvez aussi joindre
`consumer_categories` — les catégories CCPA §1798.100(b) que la personne
demande : `identifiers`, `customer_records`,
`protected_classifications`, `commercial`, `biometric`,
`internet_activity`, `geolocation`, `sensory`, `professional`,
`education`, `inferences`, `sensitive_pi`.

***

## Demandes déposées par un opérateur

### Créer une demande

`POST /compliance/dsar` — nécessite une clé API admin ou propriétaire.
Fournissez au moins un identifiant de personne (`contact_id`,
`subject_email` ou `subject_phone`) plus l'`requester_email` qui doit
recevoir la correspondance.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/compliance/dsar \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "subject_email": "jordan@example.com",
    "requester_email": "jordan@example.com",
    "applicable_jurisdiction": "gdpr",
    "request_type": "know",
    "verification_method": "email_link"
  }'
```

Retourne `202 Accepted` :

```json theme={null}
{
  "id": "dsar_8x2k…",
  "status": "received",
  "applicable_jurisdiction": "gdpr",
  "request_type": "know",
  "verification_status": "pending",
  "message": "Request received and queued for verification."
}
```

| Champ                     | Type      | Remarques                                                                                                                                                          |
| ------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `contact_id`              | string    | Optionnel. Relie la demande à un contact connu.                                                                                                                    |
| `subject_email`           | email     | Un de email / phone / contact\_id est requis.                                                                                                                      |
| `subject_phone`           | string    | E.164.                                                                                                                                                             |
| `requester_email`         | email     | **Requis.** Où les mises à jour de statut sont envoyées.                                                                                                           |
| `applicable_jurisdiction` | enum      | Par défaut `gdpr`. Doit être explicitement défini à `ccpa` ou `cpra` lorsque `request_type` vaut `opt_out_sale` ou `limit_sensitive_pi` (voir la note ci-dessous). |
| `request_type`            | enum      | Par défaut `know`.                                                                                                                                                 |
| `consumer_categories`     | string\[] | Catégories CCPA (accès uniquement).                                                                                                                                |
| `verification_method`     | enum      | `email_link`, `email_phone`, `document`, `manual_review`.                                                                                                          |
| `requester_statement`     | string    | Texte libre, ≤ 4096 caractères.                                                                                                                                    |
| `authorized_agent`        | object    | `{ agent_name, agent_email, permission_document_id? }` lorsqu'un mandataire dépose pour le compte de la personne.                                                  |

> **Note** — `applicable_jurisdiction` ne prend `gdpr` par défaut que pour
> les droits qui existent sous le RGPD. Les types de demande `opt_out_sale`
> et `limit_sensitive_pi` sont propres à CCPA/CPRA et n'ont pas
> d'équivalent RGPD ; vous devez donc définir explicitement
> `applicable_jurisdiction` à `ccpa` ou `cpra` pour eux. L'omettre (ou
> laisser le défaut `gdpr`) est rejeté avec `422 VALIDATION_ERROR`.

### Cycle de vie des statuts

Une demande progresse ainsi :

`received` → `processing` → `completed`

avec des branches terminales `failed`, `expired` et `cancelled`. Le
sous-état de **vérification** est suivi indépendamment :
`pending` → `verified` (le worker continue) ou `rejected` (le worker
s'arrête). Les lignes RGPD/déposées par un admin prennent par défaut
`not_required`.

### Vérifier ou rejeter l'identité

Les demandes à plus haute assurance (delete, opt-out, limit-sensitive)
exigent une décision d'opérateur avant que le traitement ne poursuive :

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/compliance/dsar/dsar_8x2k…/verification \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "decision": "verified", "notes": "Matched gov-ID upload." }'
```

`decision` vaut `verified` ou `rejected` ; `notes` est optionnel
(≤ 2048 caractères). Retourne le nouveau `verification_status` et
`verified_at`.

### Annuler une demande

`POST /compliance/dsar/{id}/cancel` retire une demande en cours
(RGPD Art 7(3)). Fonctionne seulement tant que la demande est `received`
ou `processing` ; une demande terminale retourne `409 Conflict`.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/compliance/dsar/dsar_8x2k…/cancel \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Duplicate of dsar_7a1f…" }'
```

### Lister et lire les demandes

* `GET /compliance/dsar` — liste paginée. Requête : `page` (≥ 1),
  `page_size` (≤ 100, défaut 25), et un filtre `status` optionnel.
* `GET /compliance/dsar/{id}` — récupère une demande. La réponse inclut
  l'`export_url` signée (et son `export_expires_at`) une fois qu'un export
  d'accès/portabilité a été produit, plus `tables_exported` décrivant les
  nombres de lignes par table.

### Demandes d'effacement

Les effacements RGPD Art 17 sont suivis comme une ressource à part, pour
que vous puissiez auditer et intervenir avant que les données soient
détruites :

* `GET /compliance/dsar/erasure-requests` — liste. Requête : `status`
  (`pending`, `cancelled`, `executing`, `executed`, `failed`) et `limit`
  (≤ 500).
* `POST /compliance/dsar/erasure-requests/{id}/cancel` — annule un
  effacement **pending** avant son exécution. `reason` optionnel
  (≤ 500 caractères). Retourne `409` s'il est déjà en exécution ou terminé.

### Tableau de bord des SLA

`GET /compliance/dsar/sla` retourne un instantané combiné des SLA export +
effacement, pour ne jamais manquer un délai légal :

```json theme={null}
{
  "items": [
    {
      "id": "dsar_8x2k…",
      "kind": "export",
      "status": "processing",
      "days_elapsed": 22,
      "days_remaining": 8,
      "severity": "amber",
      "sla_deadline_at": "2026-07-01T00:00:00.000Z",
      "approaching": true,
      "breach": false,
      "escalation_due": false
    }
  ],
  "alerts": {
    "breached": 0,
    "approaching": 1,
    "escalation_due": 0,
    "worst_severity": "amber",
    "has_alert": true
  },
  "sla_days": 30
}
```

Les niveaux de sévérité **progressent proportionnellement à la fenêtre de
SLA de chaque juridiction** — les seuils en jours sont ancrés au cas RGPD
de 30 jours et multipliés par le ratio `slaDays / 30`, de sorte qu'une
demande passe toujours en amber puis en red à la même fraction de son
propre délai. `escalation_due` bascule 5 jours avant le délai légal
(`slaDays − 5`).

Pour le **RGPD** (`sla_days: 30`) : **vert** (\< 20 jours écoulés),
**ambre** (20–25), **rouge** (26–30), **rouge + breach** (> 30) ;
`escalation_due` au jour 25.

Pour **CCPA/CPRA** (`sla_days: 45`), les mêmes ratios donnent **vert**
(\< 30), **ambre** (30–38), **rouge** (39–45), **rouge + breach** (> 45) ;
`escalation_due` au jour 40. Lisez toujours les bornes des niveaux par
rapport au `sla_days` retourné pour cette demande, pas aux chiffres fixes
20/25/30.

***

## Portail public en libre-service

Le flux public permet à une personne concernée de déposer une demande sans
compte. L'identité est prouvée par un **OTP à deux facteurs** — un code
e-mail et un code SMS — avant que toute demande soit mise en file. Les
endpoints vivent sous `/compliance/public/dsar` et ne sont pas
authentifiés, mais sont défendus par Cloudflare Turnstile, des limites de
débit par IP et par identifiant, et une forme de réponse préservant la vie
privée qui ne révèle jamais si une paire e-mail/téléphone correspond à un
contact réel.

<Note>
  Les codes de vérification SMS sont livrés via le softswitch de Devotel
  (le seul chemin SMS sortant de la plateforme). Ce sont des OTP de
  plateforme, pas du trafic facturable au tenant, et ils ne portent aucune
  persistance de reçus de livraison.
</Note>

### Aperçu du flux

<Steps>
  <Step title="Démarrer">
    `POST /compliance/public/dsar/begin` avec `email`, `phone` (E.164),
    `request_type` (`access` | `delete` | `portability` | `opt_out`), et un
    `turnstile_token` Cloudflare (requis en production). Retourne un
    `claim_id` opaque, `email_sent: true`, et `expires_in: 600`. Un OTP
    e-mail est envoyé immédiatement.
  </Step>

  <Step title="Vérifier l'e-mail">
    `POST /compliance/public/dsar/verify-email` avec `claim_id` et le
    `code` à 6 chiffres. Retourne l'état `email_verified` et l'étape
    suivante `phone_send`. Les codes expirent après 10 minutes ; 3
    tentatives max. `POST …/resend-email` (avec `claim_id` + `email`) émet
    un nouveau code, sous réserve d'un cooldown de 60 secondes.
  </Step>

  <Step title="Envoyer le code SMS">
    `POST /compliance/public/dsar/send-phone` avec `claim_id` et le `phone`
    qui correspond à celui donné au démarrage. Envoie un OTP SMS
    (`expires_in: 600`). Un cooldown de 60 secondes s'applique entre les
    envois ; une nouvelle tentative trop tôt retourne `429` avec
    `Retry-After`.
  </Step>

  <Step title="Vérifier le téléphone">
    `POST /compliance/public/dsar/verify-phone` avec `claim_id` et le
    `code` à 6 chiffres. Retourne l'état `phone_verified` et l'étape
    suivante `submit`.
  </Step>

  <Step title="Soumettre">
    `POST /compliance/public/dsar/submit` avec `claim_id`. Persiste une
    ligne d'audit et — seulement si l'e-mail + téléphone vérifiés
    correspondent à un contact de votre tenant — met en file une vraie DSAR
    (pré-marquée `verification_status: verified`, puisque l'OTP a déjà
    prouvé l'identité). Retourne un `reference_id` (par ex. `dsar_pub_…`)
    et un booléen `queued`.
  </Step>
</Steps>

### Configurer les expéditeurs de la preuve d'identité

Les deux OTP sont envoyés depuis des expéditeurs de niveau plateforme que
vous configurez une fois dans votre environnement API. Définissez-les avant
de publier le portail — un expéditeur SMS non défini sans repli fait
échouer l'étape téléphone en mode fermé.

| Variable                        | Utilisé pour                          | Défaut / repli                                                                     |
| ------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------- |
| `DEVOTEL_DSAR_PROOF_FROM_EMAIL` | Adresse d'expéditeur de l'OTP e-mail. | `privacy@orbit.devotel.io`. La livraison nécessite aussi `DEVOTEL_RESEND_API_KEY`. |
| `DEVOTEL_DSAR_PROOF_SMS_FROM`   | Expéditeur E.164 de l'OTP SMS.        | Retombe sur `DEVOTEL_PLATFORM_DEFAULT_FROM`.                                       |

Si `DEVOTEL_DSAR_PROOF_SMS_FROM` **et** `DEVOTEL_PLATFORM_DEFAULT_FROM`
sont tous deux non définis, l'étape `send-phone` **échoue fermée avec un
`503`** — le portail retourne un message « temporairement indisponible » et
l'échec est émis sous la métrique `dsar.proof.sms_send_failed` afin qu'il
apparaisse dans vos tableaux de bord plutôt que de sauter silencieusement
le second facteur. De même, l'étape e-mail retourne `503` quand
`DEVOTEL_RESEND_API_KEY` n'est pas défini. Configurez les deux expéditeurs
avant de lier publiquement le portail.

### Défenses contre les abus

| Contrôle                   | Limite                                                         |
| -------------------------- | -------------------------------------------------------------- |
| Cloudflare Turnstile       | Requis sur `begin` en production (fail-closed).                |
| `begin` par IP             | 3 par heure.                                                   |
| Cooldown par e-mail        | 1 par 60 s.                                                    |
| Cooldown SMS par téléphone | 1 par 60 s.                                                    |
| Porte Fastify par IP       | 30 requêtes/min par IP, appliquée indépendamment par endpoint. |
| TTL OTP / tentatives       | 10 minutes, 3 tentatives max par code.                         |
| TTL du claim               | 30 minutes de bout en bout.                                    |

La forme de réponse est identique, que les identifiants correspondent ou
non à un contact réel — le portail ne confirme ni n'infirme jamais que
quelqu'un se trouve dans votre base. Quand Redis est indisponible, les
portes de limitation échouent **ouvertes (fail-open)** pour préserver la
disponibilité.

### Activer la protection Turnstile

La porte Turnstile se configure avec deux variables d'environnement.

| Variable                                 | Quand         | Description                                                                                                                    |
| ---------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `DEVOTEL_TURNSTILE_SECRET_KEY`           | API (serveur) | Secret Cloudflare Turnstile. L'endpoint `begin` vérifie le `turnstile_token` soumis auprès de Cloudflare lorsqu'il est défini. |
| `NEXT_PUBLIC_DEVOTEL_TURNSTILE_SITE_KEY` | Web (client)  | Clé de site Turnstile publique avec laquelle le portail rend le widget.                                                        |

<Warning>
  La porte est **fail-open** quand `DEVOTEL_TURNSTILE_SECRET_KEY` n'est pas
  défini : `begin` accepte les requêtes sans token et consigne un seul
  avertissement. Définissez le secret en production, sinon le portail est
  sans protection Turnstile, même si toutes les autres défenses anti-abus
  ci-dessus s'appliquent toujours. Générez les deux clés dans le tableau de
  bord Cloudflare (Turnstile → Ajouter un site) et définissez-les
  respectivement sur les déploiements API et web.
</Warning>

***

## Héberger le lien du portail

Publiez le portail public dans votre politique de confidentialité comme
lien « Soumettre une demande de vie privée ». Comme le flux s'auto-vérifie
par OTP, les demandes qui arrivent par là sont déjà prouvées en identité —
elles atterrissent dans votre file d'opérateur prêtes à être traitées, et
apparaissent dans `GET /compliance/dsar` aux côtés des demandes déposées
par un opérateur.

***

## Références associées

* [Construire une posture RGPD de bout en bout](/compliance/gdpr-posture-guide) —
  où s'inscrit la réception des DSAR dans la séquence complète.
* [Gestion du consentement](/compliance/consent-management) — enregistrer
  et consulter l'état de consentement qu'une DSAR peut vous demander
  d'honorer.
* [Listes de désinscription et suppression](/compliance/opt-out-suppression) —
  comment les résultats `delete` / `opt_out` alimentent la suppression.
* [Consentement à l'enregistrement des appels](/compliance/recording-consent) —
  traiter les enregistrements référencés par une demande d'accès.
* [Référence API → Compliance](/api-reference/endpoints/compliance) —
  schémas requête/réponse complets (régénérés depuis l'API en direct).
