> ## 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.

# Solicitudes de acceso del interesado (DSAR)

> Reciba, verifique y atienda solicitudes de interesados según GDPR, CCPA, CPRA, LGPD, PDPA y DPDP en Orbit mediante flujos de trabajo de operador o el portal de autoservicio.

# Solicitudes de acceso del interesado (DSAR)

Una **solicitud de acceso del interesado** (también llamada solicitud de
privacidad o solicitud de derechos del consumidor) es el mecanismo formal
que una persona utiliza para ejercer sus derechos sobre los datos
personales que usted conserva sobre ella: el derecho de **acceso**,
**supresión**, **corrección**, **portabilidad** u **oposición a la
venta** de esos datos. La mayoría de las leyes de privacidad le imponen
un plazo estricto para responder (30 días según el GDPR, 45 según
CCPA/CPRA).

Orbit le ofrece dos vías de entrada y una canalización de cumplimiento:

* **DSAR presentada por el operador** — su equipo de soporte o
  cumplimiento presenta una solicitud en nombre de un cliente a través
  de la API autenticada o del panel.
* **Portal público de autoservicio** — el interesado presenta su propia
  solicitud mediante un flujo público y no autenticado que verifica su
  identidad con un **OTP de dos factores por correo electrónico + SMS**
  antes de poner nada en cola.

<Warning>
  Esta página describe los controles de plataforma de Orbit. **No es
  asesoramiento legal.** Sus obligaciones — qué leyes se aplican, qué
  debe divulgar y cuánto tiempo tiene — dependen de dónde residan sus
  interesados y de qué datos procese. Confírmelo con asesoría legal
  cualificada.
</Warning>

Todos los endpoints siguientes tienen su raíz en
`https://api.orbit.devotel.io/api/v1/compliance`.

***

## Jurisdicciones admitidas y plazos

El campo `applicable_jurisdiction` de una solicitud controla qué reloj
legal aplica el rastreador de SLA de Orbit. Los operadores pueden
reclasificar una solicitud tras la entrada.

| Jurisdicción                 | Código   | SLA de respuesta |
| ---------------------------- | -------- | ---------------- |
| GDPR de la UE / EEE          | `gdpr`   | 30 días          |
| CCPA de California           | `ccpa`   | 45 días          |
| CPRA de California           | `cpra`   | 45 días          |
| LGPD de Brasil               | `lgpd`   | 15 días          |
| PDPA de Singapur / Tailandia | `pdpa`   | 30 días          |
| PIPEDA de Canadá             | `pipeda` | 30 días          |
| DPDP de India                | `dpdp`   | 30 días          |

## Tipos de solicitud

`request_type` describe lo que solicita el interesado. El conjunto
completo de verbos de CCPA/CPRA está disponible para los operadores; el
portal público expone un subconjunto más sencillo que se corresponde con
él.

| `request_type` del operador | Significado                                                           | Verbo del portal público |
| --------------------------- | --------------------------------------------------------------------- | ------------------------ |
| `know`                      | Acceso: divulgar los datos conservados (GDPR Art. 15, CCPA §1798.110) | `access`                 |
| `delete`                    | Supresión (GDPR Art. 17, CCPA §1798.105)                              | `delete`                 |
| `correct`                   | Rectificación (GDPR Art. 16, CPRA §1798.106)                          | —                        |
| `portability`               | Exportación legible por máquina (GDPR Art. 20)                        | `portability`            |
| `opt_out_sale`              | Oposición a la venta/divulgación (CCPA §1798.120)                     | `opt_out`                |
| `limit_sensitive_pi`        | Limitar el uso de PI sensible (CPRA §1798.121)                        | —                        |
| `non_discrimination`        | Derecho de no discriminación (CCPA §1798.125)                         | —                        |

Para las solicitudes de acceso CCPA también puede adjuntar
`consumer_categories`: las categorías de CCPA §1798.100(b) sobre las que
pregunta el interesado: `identifiers`, `customer_records`,
`protected_classifications`, `commercial`, `biometric`,
`internet_activity`, `geolocation`, `sensory`, `professional`,
`education`, `inferences`, `sensitive_pi`.

***

## Solicitudes presentadas por operadores

### Crear una solicitud

`POST /compliance/dsar` — requiere una clave de API de administrador o
propietario. Proporcione al menos un identificador del interesado
(`contact_id`, `subject_email` o `subject_phone`) además del
`requester_email` que debe recibir la correspondencia.

```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"
  }'
```

Devuelve `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."
}
```

| Campo                     | Tipo      | Notas                                                                                                                                                                      |
| ------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `contact_id`              | string    | Opcional. Vincula la solicitud a un contacto conocido.                                                                                                                     |
| `subject_email`           | email     | Se requiere uno de: email / phone / contact\_id.                                                                                                                           |
| `subject_phone`           | string    | E.164.                                                                                                                                                                     |
| `requester_email`         | email     | **Obligatorio.** Donde se envían las actualizaciones de estado.                                                                                                            |
| `applicable_jurisdiction` | enum      | Valor predeterminado `gdpr`. Debe establecerse explícitamente en `ccpa` o `cpra` cuando `request_type` es `opt_out_sale` o `limit_sensitive_pi` (véase la nota siguiente). |
| `request_type`            | enum      | Valor predeterminado `know`.                                                                                                                                               |
| `consumer_categories`     | string\[] | Categorías CCPA (solo acceso).                                                                                                                                             |
| `verification_method`     | enum      | `email_link`, `email_phone`, `document`, `manual_review`.                                                                                                                  |
| `requester_statement`     | string    | Texto libre, ≤ 4096 caracteres.                                                                                                                                            |
| `authorized_agent`        | object    | `{ agent_name, agent_email, permission_document_id? }` cuando un agente presenta la solicitud en nombre del interesado.                                                    |

> **Nota** — `applicable_jurisdiction` solo adopta el valor
> predeterminado `gdpr` para derechos que existen bajo el GDPR. Los
> tipos de solicitud `opt_out_sale` y `limit_sensitive_pi` son
> exclusivos de CCPA/CPRA y no tienen equivalente en el GDPR, por lo
> que debe establecer `applicable_jurisdiction` en `ccpa` o `cpra`
> explícitamente para ellos. Omitirlo (o dejar el valor predeterminado
> `gdpr`) se rechaza con `422 VALIDATION_ERROR`.

### Ciclo de vida del estado

Una solicitud atraviesa:

`received` → `processing` → `completed`

con las ramas terminales `failed`, `expired` y `cancelled`. El
subestado de **verificación** se rastrea de forma independiente:
`pending` → `verified` (el trabajador continúa) o `rejected` (el
trabajador se detiene). Las filas presentadas por administradores/GDPR
tienen el valor predeterminado `not_required`.

### Verificar o rechazar la identidad

Las solicitudes de mayor garantía (delete, opt-out, limit-sensitive)
requieren una decisión del operador antes de que prosiga el cumplimiento:

```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` es `verified` o `rejected`; `notes` es opcional
(≤ 2048 caracteres). Devuelve el nuevo `verification_status` y
`verified_at`.

### Cancelar una solicitud

`POST /compliance/dsar/{id}/cancel` retira una solicitud en curso
(GDPR Art. 7(3)). Solo funciona mientras la solicitud está en `received`
o `processing`; una solicitud terminal devuelve `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…" }'
```

### Listar y leer solicitudes

* `GET /compliance/dsar` — lista paginada. Consulta: `page` (≥ 1),
  `page_size` (≤ 100, valor predeterminado 25) y un filtro `status`
  opcional.
* `GET /compliance/dsar/{id}` — obtiene una solicitud. La respuesta
  incluye la `export_url` firmada (y su `export_expires_at`) una vez
  producida una exportación de acceso/portabilidad, además de
  `tables_exported`, que describe los recuentos de filas por tabla.

### Solicitudes de supresión

Las supresiones del GDPR Art. 17 se rastrean como un recurso propio, de
modo que pueda auditar e intervenir antes de que se destruyan los datos:

* `GET /compliance/dsar/erasure-requests` — lista. Consulta: `status`
  (`pending`, `cancelled`, `executing`, `executed`, `failed`) y
  `limit` (≤ 500).
* `POST /compliance/dsar/erasure-requests/{id}/cancel` — cancela una
  supresión **pendiente** antes de que se ejecute. `reason` opcional
  (≤ 500 caracteres). Devuelve `409` si ya se está ejecutando o ha
  finalizado.

### Panel de SLA

`GET /compliance/dsar/sla` devuelve una instantánea combinada del SLA de
exportación + supresión para que nunca pierda un plazo legal:

```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
}
```

Los niveles de severidad **escalan proporcionalmente con la ventana de
SLA de cada jurisdicción**: los umbrales de días se anclan al caso GDPR
de 30 días y se multiplican por la razón `slaDays / 30`, por lo que una
solicitud siempre pasa a ámbar y rojo en la misma fracción de su propio
plazo. `escalation_due` se activa 5 días antes del plazo legal
(`slaDays − 5`).

Para **GDPR** (`sla_days: 30`): **verde** (\< 20 días transcurridos),
**ámbar** (20–25), **rojo** (26–30), **rojo + incumplimiento** (> 30);
`escalation_due` en el día 25.

Para **CCPA/CPRA** (`sla_days: 45`) las mismas razones dan **verde**
(\< 30), **ámbar** (30–38), **rojo** (39–45), **rojo + incumplimiento**
(> 45); `escalation_due` en el día 40. Lea siempre los límites de nivel
contra el `sla_days` devuelto para esa solicitud, no contra los números
fijos 20/25/30.

***

## Portal público de autoservicio

El flujo público permite a un interesado presentar una solicitud sin una
cuenta. La identidad se verifica con un **OTP de dos factores** — un
código por correo electrónico y un código por SMS — antes de poner en
cola cualquier solicitud. Los endpoints se encuentran bajo
`/compliance/public/dsar` y no están autenticados, pero están protegidos
por Cloudflare Turnstile, límites de tasa por IP y por identificador, y
una forma de respuesta que preserva la privacidad y nunca revela si un
par correo electrónico/teléfono coincide con un contacto real.

<Note>
  Los códigos de verificación por SMS se entregan a través del
  softswitch de Devotel (la única ruta de SMS saliente de la
  plataforma). Son OTP de plataforma, no tráfico facturable al tenant, y
  no conservan recibos de entrega.
</Note>

### Resumen del flujo

<Steps>
  <Step title="Inicio">
    `POST /compliance/public/dsar/begin` con `email`, `phone` (E.164),
    `request_type` (`access` | `delete` | `portability` | `opt_out`) y
    un `turnstile_token` de Cloudflare (obligatorio en producción).
    Devuelve un `claim_id` opaco, `email_sent: true` y
    `expires_in: 600`. Un OTP por correo electrónico se envía de
    inmediato.
  </Step>

  <Step title="Verificar correo electrónico">
    `POST /compliance/public/dsar/verify-email` con `claim_id` y el
    `code` de 6 dígitos. Devuelve el estado `email_verified` y el
    siguiente paso `phone_send`. Los códigos caducan a los 10 minutos;
    máximo 3 intentos. `POST …/resend-email` (con `claim_id` + `email`)
    emite un nuevo código, sujeto a un enfriamiento de 60 segundos.
  </Step>

  <Step title="Enviar código al teléfono">
    `POST /compliance/public/dsar/send-phone` con `claim_id` y el
    `phone` que coincide con el indicado en begin. Envía un OTP por SMS
    (`expires_in: 600`). Se aplica un enfriamiento de 60 segundos entre
    envíos; un reintento prematuro devuelve `429` con `Retry-After`.
  </Step>

  <Step title="Verificar teléfono">
    `POST /compliance/public/dsar/verify-phone` con `claim_id` y el
    `code` de 6 dígitos. Devuelve el estado `phone_verified` y el
    siguiente paso `submit`.
  </Step>

  <Step title="Enviar">
    `POST /compliance/public/dsar/submit` con `claim_id`. Persiste una
    fila de auditoría y — solo si el correo electrónico + teléfono
    verificados coinciden con un contacto de su tenant — pone en cola
    un DSAR real (premarcado como `verification_status: verified`, ya
    que el OTP ya probó la identidad). Devuelve un `reference_id`
    (p. ej., `dsar_pub_…`) y un booleano `queued`.
  </Step>
</Steps>

### Configurar los remitentes de la prueba de identidad

Los dos OTP se envían desde remitentes de nivel de plataforma que usted
configura una vez en su entorno de API. Establézcalos antes de publicar
el portal: un remitente SMS sin configurar y sin fallback hace que el
paso del teléfono falle en modo cerrado (fail-closed).

| Variable                        | Se usa para                                      | Valor predeterminado / fallback                                                   |
| ------------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------------- |
| `DEVOTEL_DSAR_PROOF_FROM_EMAIL` | Dirección From en el OTP por correo electrónico. | `privacy@orbit.devotel.io`. La entrega también necesita `DEVOTEL_RESEND_API_KEY`. |
| `DEVOTEL_DSAR_PROOF_SMS_FROM`   | Remitente E.164 en el OTP por SMS.               | Recurre a `DEVOTEL_PLATFORM_DEFAULT_FROM`.                                        |

Si `DEVOTEL_DSAR_PROOF_SMS_FROM` **y** `DEVOTEL_PLATFORM_DEFAULT_FROM`
están ambos sin configurar, el paso `send-phone` **falla en modo cerrado
(fail-closed) con un `503`** — el portal devuelve un mensaje de "no
disponible temporalmente" y el fallo se emite bajo la métrica
`dsar.proof.sms_send_failed` para que aparezca en sus paneles en lugar
de omitir silenciosamente el segundo factor. Del mismo modo, el paso del
correo electrónico devuelve `503` cuando `DEVOTEL_RESEND_API_KEY` no
está configurada. Configure ambos remitentes antes de enlazar el portal
públicamente.

### Defensas contra abuso

| Control                             | Límite                                                               |
| ----------------------------------- | -------------------------------------------------------------------- |
| Cloudflare Turnstile                | Obligatorio en `begin` en producción (fail-closed).                  |
| `begin` por IP                      | 3 por hora.                                                          |
| Enfriamiento por correo electrónico | 1 cada 60 s.                                                         |
| Enfriamiento de SMS por teléfono    | 1 cada 60 s.                                                         |
| Puerta de IP de Fastify             | 30 solicitudes/min por IP, aplicada independientemente por endpoint. |
| TTL / intentos del OTP              | 10 minutos, máximo 3 intentos por código.                            |
| TTL del claim                       | 30 minutos de extremo a extremo.                                     |

La forma de respuesta es idéntica se cumpla o no la coincidencia de los
identificadores con un contacto real: el portal nunca confirma ni niega
que alguien esté en su base de datos. Cuando Redis no está disponible,
las puertas de límite de tasa fallan en modo **abierto** (fail-open)
para preservar la disponibilidad.

### Habilitar la protección de Turnstile

La puerta de Turnstile se configura con dos variables de entorno.

| Variable                                 | Cuándo         | Descripción                                                                                                                           |
| ---------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `DEVOTEL_TURNSTILE_SECRET_KEY`           | API (servidor) | Secreto de Cloudflare Turnstile. El endpoint `begin` verifica el `turnstile_token` enviado contra Cloudflare cuando está configurada. |
| `NEXT_PUBLIC_DEVOTEL_TURNSTILE_SITE_KEY` | Web (cliente)  | Clave pública de sitio de Turnstile con la que el portal renderiza el widget.                                                         |

<Warning>
  La puerta es **fail-open** cuando `DEVOTEL_TURNSTILE_SECRET_KEY` no
  está configurada: `begin` acepta solicitudes sin token y registra una
  única advertencia. Establezca el secreto en producción, o el portal
  quedará desprotegido por Turnstile aunque todas las demás defensas
  contra abuso anteriores sigan aplicándose. Genere ambas claves en el
  panel de Cloudflare (Turnstile → Add site) y establézcalas en los
  despliegues de API y web respectivamente.
</Warning>

***

## Alojar el enlace del portal

Publique el portal público en su política de privacidad como el enlace
"Enviar una solicitud de privacidad". Como el flujo se verifica
automáticamente mediante OTP, las solicitudes que llegan por él ya
tienen la identidad probada: llegan a su cola de operador listas para su
cumplimiento y aparecen en `GET /compliance/dsar` junto a las
solicitudes presentadas por operadores.

***

## Referencias relacionadas

* [Cómo montar una postura GDPR de extremo a extremo](/compliance/gdpr-posture-guide) —
  dónde se sitúa la entrada de DSAR en la secuencia completa.
* [Gestión del consentimiento](/compliance/consent-management) — registre
  y consulte el estado de consentimiento que un DSAR puede pedirle que
  respete.
* [Listas de exclusión y supresión](/compliance/opt-out-suppression) —
  cómo los resultados de `delete` / `opt_out` fluyen hacia la supresión.
* [Consentimiento para la grabación de llamadas](/compliance/recording-consent) — tratamiento
  de grabaciones a las que hace referencia una solicitud de acceso.
* [Referencia de API → Compliance](/api-reference/endpoints/compliance) — esquemas
  completos de solicitud/respuesta (regenerados a partir de la API en
  vivo).
