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

# Datos de prueba del sandbox: escenarios y valores mágicos

> Prueba Didit en sandbox: escenarios, valores mágicos, endpoint arm, documentos y códigos OTP con resultados deterministas, sin PII ni facturación.

El **sandbox** de Didit es el equivalente a las tarjetas de prueba de Stripe o a las plantillas de documentos de Sumsub: te permite reproducir cualquier resultado de verificación de forma determinista, sin tocar proveedores reales, datos personales (PII) reales ni tu saldo.

> [!NOTE]
>
> La fuente de verdad de estos datos es `serialize_catalog()` en el servicio de verificación, que está disponible en tiempo real en `GET /v1/sandbox/scenarios/`. Se genera una copia sometida a pruebas de detección de desviaciones en `docs/sandbox/SANDBOX_REFERENCE.md`, dentro del repositorio `service-didit-verification`, mediante `pipenv run python scripts/generate_sandbox_reference.py`. Esta página refleja esa referencia: si algo de aquí parece desactualizado, confía siempre en el endpoint del catálogo en tiempo real. El endpoint del catálogo requiere autenticación: envía tu clave de API (`x-api-key`) igual que en cualquier otra llamada, o un token de sesión si lo consultas desde dentro de una sesión en curso.

## Modelo de entornos

El sandbox es un **modo por aplicación** (`ApplicationDetail.mode == SANDBOX`), la misma idea que el modo de prueba de Stripe. Mientras una sesión se ejecuta en una aplicación de sandbox:

* Todos los proveedores externos (OCR, AML, coincidencia facial, prueba de vida, NFC, teléfono, correo electrónico, IP, POA, validación de base de datos, Document AI) están **simulados**: nunca se llama a ningún tercero.
* **La prueba de vida se ejecuta exactamente igual que en producción.** Un flujo con prueba de vida activa ejecuta la captura activa real en el sandbox, tanto en el flujo alojado como a través de la transferencia al flujo web de los SDK nativos, para que puedas probar la experiencia que tendrán tus usuarios. Solo el *resultado* de la prueba de vida se simula, mediante el escenario o el valor mágico, y el informe registra el método que configura tu flujo.
* **Los dominios personalizados solo existen en producción.** Una aplicación de sandbox no puede añadir un [dominio personalizado](https://docs.didit.me/es/console/custom-domain.md); las sesiones de sandbox siempre se ejecutan en el dominio predeterminado de verificación alojada.
* Las sesiones **nunca se cobran** y se omite la comprobación de saldo.
* Se aplica una cuota por aplicación de **500 creaciones de sesión cada 24 h**.
* Los webhooks incluyen `"environment": "sandbox"` (las sesiones en producción incluyen `"environment": "live"`).
* El flujo alojado expone el entorno de la sesión para que la interfaz pueda advertir a los usuarios de que no suban datos personales (PII) reales.
* **Los resultados nunca dependen de los píxeles**: los datos extraídos y los estados provienen del escenario o del valor mágico. Los archivos capturados en sí (documentos, selfie, vídeo, archivos de POA) se almacenan de verdad, exactamente igual que en una sesión en producción.

## Monitorización de transacciones en sandbox

El sandbox está **diseñado en torno a la sesión**: simula los proveedores a los que
llamaría una sesión de verificación. La Monitorización de transacciones no tiene ningún
proveedor que simular, así que una aplicación de sandbox sigue el otro camino: la
transacción se guarda, pero **nunca se ejecuta sobre ella nada que decida, analice,
notifique o cobre**.

Una transacción creada en una aplicación de sandbox se guarda y se devuelve como siempre,
y no le ocurre nada de lo siguiente:

* **Sin evaluación de reglas.** `rule_runs` está vacío, sea cual sea el modo de tus reglas. Una regla `ACTIVE` en una aplicación de sandbox nunca se activa, así que la transacción se queda en `APPROVED` con puntuación `0` y no se abre ninguna alerta ni ningún caso.
* **Sin análisis AML ni análisis de wallets.** `provider_results` está vacío incluso en transacciones cripto de una aplicación con la monitorización activada.
* **Sin webhooks.** No se entrega ni `transaction.created` ni `transaction.status.updated` para una transacción de sandbox.
* **Sin facturación.** Se registra un evento de uso con precio `0.0000` y tu saldo no se modifica.

Cada respuesta de transacción incluye un campo `environment` (`"live"` o `"sandbox"`)
para que un `rule_runs` vacío nunca resulte ambiguo: `"sandbox"` significa que las reglas no se
evaluaron, no que ninguna coincidiera. La Business Console indica lo mismo
en la tarjeta *Risk & decision* de la transacción, en lugar de un simple "No rules
evaluated".

> [!NOTE]
>
> **Travel Rule es la única excepción a "no se ejecuta nada".** En una aplicación con
> Travel Rule activado, una transacción `travelRule` de sandbox sigue ejecutando el motor
> de intercambio: se crea el registro de la transferencia y este avanza por sus estados.
> Lo que el sandbox nunca hace es comunicarse con nadie fuera de Didit.
> Su única contraparte es la [Didit Test VASP](https://docs.didit.me/es/transaction-monitoring/travel-rule-sandbox.md), integrada en la plataforma: ningún mensaje de cumplimiento llega a un VASP de contraparte real por ningún canal (`TRP`, una membresía de red que hayas conectado, correo electrónico o `INTERNAL`), se omite el análisis de atribución del VASP de contraparte y, al cancelar, no se envía nada a ninguna contraparte.
> Los webhooks `travel_rule.status.updated` se entregan marcados con `"environment": "sandbox"`.
>
> **Saliente** - una transacción `travelRule` que envías tú.
> La comprobación de `threshold_amount` se ejecuta primero y, en sandbox, solo se aplica cuando envías `transaction_details.amount_in_default_currency`, porque el sandbox no convierte divisas; sin ese campo, toda transferencia de sandbox se considera por encima del umbral.
> Una transferencia sin respuesta expira tras `confirmation_timeout_hours` y, a continuación, se aplica tu `timeout_outcome` a la transacción.
> Por encima del umbral, la comprobación de wallets sin custodia se aplica con normalidad: una transferencia a una [wallet sin custodia declarada](https://docs.didit.me/es/transaction-monitoring/travel-rule.md#wallets-sin-custodia-declaradas) pasa a `UNCONFIRMED_OWNERSHIP` sin que se envíe nada, y puedes completarla en sandbox mediante el [widget de propiedad de wallet](https://docs.didit.me/es/transaction-monitoring/wallet-ownership-widget.md), o mediante [confirmar la propiedad](https://docs.didit.me/es/management-api/travel-rule/confirm-ownership.md) cuando la wallet de destino ya está en tu libreta de direcciones de wallets.
> Cualquier otra transferencia por encima del umbral va a la Didit Test VASP por el canal `TEST_VASP`, que responde a ella dentro de Didit con el escenario que elijas mediante la wallet o el nombre del beneficiario: se completa, se rechaza, presenta una discrepancia, pasa a `COUNTERPARTY_VASP_NOT_REACHABLE`, expira (y entonces se aplica tu `timeout_outcome` a la transacción) o queda a la espera de la propiedad de la wallet.
> Consulta [Probar Travel Rule en sandbox](https://docs.didit.me/es/transaction-monitoring/travel-rule-sandbox.md) para ver todos los escenarios.
>
> **Entrante** - `POST /v3/travel-rule/inbound/`. El registro nunca envía nada por un
> canal, así que no se simula nada: el sandbox resuelve el depósito contra tu
> libreta de direcciones de wallets y lo liquida de forma síncrona, exactamente igual que en producción. Se resuelve en
> un resultado real en lugar de `AWAITING_COUNTERPARTY`: `ON_HOLD` (sin coincidencia
> en la libreta de direcciones), `UNCONFIRMED_OWNERSHIP` (no se ha verificado la propiedad de la entrada coincidente),
> `COUNTERPARTY_MISMATCHED_DATA` (el nombre del beneficiario no coincide con el titular de la wallet)
> o `COMPLETED` y después `FINISHED` (coincidió con una entrada verificada).
> Para recibir en su lugar una transferencia de la Didit Test VASP, usa [Disparar una transferencia entrante de la Test VASP](https://docs.didit.me/es/management-api/travel-rule/trigger-test-vasp-inbound.md) con una de las wallets de tu propia libreta de direcciones.

### Escenarios de la Test VASP de Travel Rule

El campo `travel_rule.scenarios` del catálogo enumera estos escenarios salientes. Indica el nombre del beneficiario que aparece abajo en una transacción `travelRule` saliente después de activar Travel Rule en tu aplicación de sandbox. Las comprobaciones del umbral y de wallets sin custodia declaradas descritas arriba se ejecutan primero.

| Escenario | Nombre del beneficiario | Resultado |
| - | - | - |
| `accept` | `Didit Test VASP` | `COMPLETED` |
| `decline` | `Didit Test VASP Decline` | `COUNTERPARTY_VASP_GENERAL_DECLINE` |
| `name_mismatch` | `Didit Test VASP Name Mismatch` | `COUNTERPARTY_MISMATCHED_DATA` |
| `not_reachable` | `Didit Test VASP Not Reachable` | `COUNTERPARTY_VASP_NOT_REACHABLE` tras los reintentos de entrega |
| `timeout` | `Didit Test VASP Timeout` | `EXPIRED` cuando la comprobación de expiración se ejecuta después del plazo de un minuto |
| `self_hosted` | `Didit Test VASP Self Hosted` | `UNCONFIRMED_OWNERSHIP` |

También puedes elegir un escenario con la dirección de la wallet del beneficiario en lugar del nombre (la wallet prevalece cuando se indican ambos), y una transferencia que no indica ninguno de los dos se completa como `accept`. [Probar Travel Rule en sandbox](https://docs.didit.me/es/transaction-monitoring/travel-rule-sandbox.md) enumera la wallet de cada escenario y su resultado exacto.

El [disparador entrante de la Test VASP](https://docs.didit.me/es/management-api/travel-rule/trigger-test-vasp-inbound.md) solo acepta `accept` y `name_mismatch` en su campo `scenario`.

> [!NOTE]
>
> **Cómo poner a prueba una regla antes de que entre en producción.** Usa
> [Backtest](https://docs.didit.me/es/transaction-monitoring/rules-api.md#crear-hacer-backtest-y-activar): vuelve a ejecutar
> la regla sobre tu historial real de transacciones e informa de `evaluated` /
> `matched` / `affected_entities` sin modificar el estado de ninguna transacción. Combínalo
> con el modo `TEST` en la aplicación de producción (una regla `TEST` se evalúa en cada nueva
> transacción de producción y registra las coincidencias que habría encontrado, sin cambiar la
> puntuación ni el estado) y cambia a `ACTIVE` cuando la selectividad sea la adecuada.

## Cómo cambiar entre producción y sandbox

Producción y sandbox son **aplicaciones separadas**, de modo que el tráfico de prueba y los datos de producción nunca se mezclan. Cada aplicación tiene un `mode` con el valor `live` o `sandbox`:

* Usa la clave de API de una aplicación en **modo sandbox** para obtener sesiones simulables, sin facturación y con resultados simulados.
* Usa la clave de API de una aplicación en **modo producción** para verificaciones reales.

El campo `sandbox_scenario` (al crear la sesión y en la API de AML independiente) y los endpoints de simulación solo se aceptan en aplicaciones sandbox; en las aplicaciones de producción se rechazan.

## Sandbox en el flujo de verificación alojado

Las sesiones creadas en una aplicación sandbox muestran elementos adicionales de interfaz en el flujo de verificación alojado (la `url` que devuelve `POST /v3/session/`):

* **Banner de sandbox dentro de la tarjeta.** Cada pantalla muestra un banner dentro de la tarjeta, "Solo datos de prueba - sin llamadas reales", para que siempre quede claro que la sesión no realiza llamadas a proveedores reales ni afecta a tu saldo. Los archivos capturados se almacenan como en cualquier sesión, así que usa los documentos de muestra y los datos de prueba en lugar de documentos de identidad o información personal reales.
* **Selector de escenario previo al flujo.** Antes de que empiece la captura, un selector dentro de la tarjeta guía al usuario para elegir un resultado de prueba: primero elige un resultado y luego un disparador para ese resultado. Al confirmar, se invoca `POST /v3/session/{session_id}/sandbox/arm/` (consulta [Activar (o reactivar) la sesión antes de que empiece el flujo](#3-armar-o-volver-a-armar-la-sesión-antes-de-que-empiece-el-flujo) más abajo) y el flujo continúa con los valores mágicos de ese escenario ya aplicados.
* **Franja de documentos de muestra.** Una única fila de documentos de muestra de sandbox (hasta 3) aparece justo debajo del componente de carga o captura de documentos. Toca uno para usarlo en lugar de subir un documento real.

Las sesiones sandbox almacenan los archivos capturados **reales**: los documentos, la selfie, el video de prueba de vida y los archivos de prueba de domicilio se suben y se conservan exactamente igual que en una sesión de producción. Los datos *extraídos* (campos de verificación de identidad, puntuaciones de coincidencia facial, resultados de prueba de vida y similares) y el propio resultado de la sesión (Aprobado / En revisión / Rechazado) los simula el escenario o el valor mágico, y nunca se obtienen de los archivos capturados. La Business Console marca las secciones de datos extraídos de una sesión sandbox con una etiqueta **Datos simulados**, para que los revisores distingan de un vistazo qué partes de la sesión son la captura real y cuáles dependen del escenario.

## Escenarios

Cada escenario es un conjunto con nombre de valores mágicos. Pasa su slug como `sandbox_scenario` al crear la sesión (o al endpoint de simulación) para reproducir el resultado.

| Slug | Título | Estado predeterminado | Descripción |
| - | - | - | - |
| `approve` | Flujo ideal: todas las funciones devuelven APPROVED | Aprobado | Comportamiento predeterminado del sandbox: cada función simulada devuelve un resultado APPROVED sin incidencias. La sesión termina con estado APPROVED. |
| `decline_aml_hit` | AML: persona expuesta políticamente | Rechazado | La simulación de AML devuelve una coincidencia y la sesión se rechaza en la función de AML. La coincidencia usa el mismo [esquema de Hit](https://docs.didit.me/es/core-technology/aml-screening/report-aml-screening.md#campos-por-hit) que los proveedores de AML en producción (`url`, `match_score`, `risk_score`, `review_status`, `properties`, `pep_matches[]` y `score_breakdown` vienen rellenados) y se escribe un Log de advertencia POSSIBLE\_MATCH\_FOUND. |
| `decline_document_expired` | Verificación de identidad: documento expirado | Rechazado | La simulación de verificación de identidad devuelve un pasaporte cuya fecha de caducidad ya pasó. La sesión se rechaza en la verificación de identidad con un Log de advertencia DOCUMENT\_EXPIRED. |
| `decline_minimum_age` | Verificación de identidad: por debajo de la política de edad mínima | Rechazado | La simulación de verificación de identidad devuelve una fecha de nacimiento que hace que el usuario tenga unos 10 años. La comprobación de edad de producción emite MINIMUM\_AGE\_NOT\_MET y el flujo rechaza la sesión. |
| `decline_mrz_validation` | Verificación de identidad: fallo en el dígito de control de la MRZ | Rechazado | La simulación de verificación de identidad devuelve un pasaporte con un dígito de control de la MRZ corrupto. El validador de MRZ de producción emite MRZ\_VALIDATION\_FAILED y la sesión se rechaza. |
| `decline_could_not_recognize_document` | Verificación de identidad: documento ilegible | Rechazado | La simulación de verificación de identidad devuelve una respuesta vacía. Producción genera COULD\_NOT\_RECOGNIZE\_DOCUMENT y lo transmite al SDK. |
| `decline_face_match_low_similarity` | Coincidencia facial: similitud por debajo del umbral de rechazo | Rechazado | La simulación de coincidencia facial devuelve una puntuación baja. Producción emite LOW\_FACE\_MATCH\_SIMILARITY y la asignación de acciones del nodo del flujo rechaza la sesión. |
| `decline_database_no_match` | Validación de base de datos: sin coincidencia en ningún servicio | Rechazado | Cada servicio de base de datos seleccionado devuelve NO\_MATCH. Producción emite DATABASE\_VALIDATION\_NO\_MATCH y el flujo rechaza la sesión. |
| `decline_phone_high_risk` | Teléfono: número de prueba reservado de alto riesgo | Rechazado | La simulación de verificación de teléfono bloquea la verificación (rango de prueba reservado). La verificación de teléfono termina como BLOCKED y emite HIGH\_RISK\_PHONE\_NUMBER. |
| `decline_email_breach` | Correo electrónico: coincidencia en base de datos de filtraciones | Rechazado | La simulación de filtraciones de correo devuelve una coincidencia de filtración para la dirección. La verificación de correo electrónico emite BREACHED\_EMAIL\_DETECTED. |
| `decline_ip_blocklist` | Análisis de IP: dirección IP en la lista de bloqueo | Rechazado | La simulación de análisis de IP rechaza la ubicación con IP\_ADDRESS\_IN\_BLOCKLIST. La sesión se rechaza en el análisis de IP. |
| `decline_poa_address_mismatch` | Prueba de domicilio: la dirección no coincide con la proporcionada | Rechazado | La simulación de prueba de domicilio rechaza la prueba de domicilio con ADDRESS\_MISMATCH\_WITH\_PROVIDED. La sesión se rechaza en la prueba de domicilio. |
| `decline_liveness_attack` | Prueba de vida: ataque facial detectado | Rechazado | La simulación de prueba de vida pasiva detecta una señal de ataque facial y emite LIVENESS\_FACE\_ATTACK. La sesión se rechaza en la prueba de vida. |
| `decline_nfc_chip_not_verified` | NFC: chip del pasaporte electrónico no verificado | Rechazado | La simulación de pasaporte electrónico devuelve un chip cuya firma SOD/DG no supera la comprobación de integridad. Producción emite NFC\_CHIP\_NOT\_VERIFIED y la sesión se rechaza en NFC cuando el flujo rechaza los chips no verificados. |
| `decline_nfc_trust_anchor_missing` | NFC: ancla de confianza emisora no disponible | Rechazado | La simulación de pasaporte electrónico devuelve un chip firmado por un DSC cuya CSCA emisora no está en el almacén de confianza. Producción emite NFC\_TRUST\_ANCHOR\_MISSING y la sesión se rechaza cuando el flujo rechaza los chips no verificados. |
| `decline_document_ai_unreadable` | Document AI: documento ilegible | Rechazado | La simulación de Document AI no devuelve ningún campo legible. Producción emite DOCUMENT\_AI\_UNREADABLE\_DOCUMENT y el documento queda como rechazado una vez agotado el límite de reintentos. |
| `review_aml_possible_match` | AML: posible coincidencia que requiere revisión | En revisión | La simulación de AML devuelve una coincidencia de menor confianza. La sesión queda En revisión con un Log de nivel de advertencia POSSIBLE\_MATCH\_FOUND para que un analista lo resuelva. |
| `review_face_match_borderline` | Coincidencia facial: similitud límite | En revisión | La simulación de coincidencia facial devuelve una puntuación entre los umbrales de revisión y de rechazo. La sesión queda En revisión en la coincidencia facial. |
| `review_document_borderline_age` | Verificación de identidad: menor de edad enviado a revisión | En revisión | La simulación de verificación de identidad devuelve una fecha de nacimiento inferior a la edad mínima configurada en el flujo. El minimum\_age\_action del flujo está configurado como REVIEW en lugar de DECLINE, así que la sesión queda En revisión en la verificación de identidad en vez de rechazarse. |
| `review_poa_partial_match` | Prueba de domicilio: coincidencia parcial de la dirección | En revisión | La simulación de prueba de domicilio devuelve una dirección que solo coincide parcialmente con la proporcionada. La sesión queda En revisión en la prueba de domicilio. |
| `review_database_partial_match` | Validación de base de datos: coincidencia parcial | En revisión | Los servicios de base de datos devuelven coincidencias parciales en el nombre y la fecha de nacimiento. La sesión queda En revisión en la validación de base de datos. |
| `decline_kyb_registry_mismatch` | KYB: los datos del registro no coinciden | Rechazado | La simulación del registro devuelve una empresa cuyos datos registrales no coinciden con los datos esperados. La sesión de empresa se rechaza en la comprobación del registro. |
| `review_kyb_registry_partial` | KYB: los datos del registro coinciden parcialmente | En revisión | La simulación del registro devuelve una empresa cuyo nombre solo coincide parcialmente. La sesión de empresa queda En revisión en la comprobación del registro. |

Los escenarios `review_*` existen para que puedas probar la experiencia de revisión manual (la cola de revisión de la consola, los webhooks `In Review`, las acciones de aprobar o rechazar del revisor) sin necesitar una entrada que provoque un rechazo. Los dos escenarios `kyb_*` anteriores definen `expected_details.registration_number` en lugar de `expected_details.first_name`: solo se aplican a sesiones de Verificación de empresas (KYB).

## Valores mágicos

Un valor mágico es una entrada concreta que hace que una función simulada emita un riesgo concreto. Para activarlo, asigna el valor mágico al campo de entrada (directamente o mediante un escenario).

| Función | Campo de entrada | Valor mágico | Riesgo | Descripción |
| - | - | - | - | - |
| AML | `user_email` | `sandbox+aml_hit@didit.test` | `POSSIBLE_MATCH_FOUND` | AML — persona expuesta políticamente; la simulación de AML devuelve una coincidencia y la sesión se rechaza en AML. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_DOCUMENT_EXPIRED` | `DOCUMENT_EXPIRED` | OCR — documento expirado; la sesión se rechaza en la verificación de identidad. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_MINIMUM_AGE_NOT_MET` | `MINIMUM_AGE_NOT_MET` | OCR — según la fecha de nacimiento, el usuario no alcanza la edad mínima configurada; la sesión se rechaza. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_MAXIMUM_AGE_EXCEEDED` | `MAXIMUM_AGE_EXCEEDED` | OCR — la fecha de nacimiento extraída indica una edad superior a la permitida por la política de edad máxima configurada. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_DOCUMENT_NUMBER_FORMAT_MISMATCH` | `DOCUMENT_NUMBER_FORMAT_MISMATCH` | OCR — el número de documento extraído no coincide con la expresión regular de formato esperada para ese documento según el registro de documentos. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_PERSONAL_NUMBER_FORMAT_MISMATCH` | `PERSONAL_NUMBER_FORMAT_MISMATCH` | OCR — el número personal extraído no supera la comprobación de formato del registro de documentos. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION` | `DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION` | OCR — el tipo de documento extraído queda fuera de la política `documents_allowed` de la aplicación. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_FULL_NAME_MISMATCH_WITH_PROVIDED` | `FULL_NAME_MISMATCH_WITH_PROVIDED` | OCR — el nombre del documento no coincide, por encima del umbral de coincidencia de nombres, con el nombre que el cliente rellenó previamente en `expected_details`. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_GENDER_MISMATCH_WITH_PROVIDED` | `GENDER_MISMATCH_WITH_PROVIDED` | OCR — el género extraído no coincide con `expected_details.gender`. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_DOB_MISMATCH_WITH_PROVIDED` | `DOB_MISMATCH_WITH_PROVIDED` | OCR — la fecha de nacimiento extraída no coincide con `expected_details.date_of_birth`. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_COUNTRY_MISMATCH_WITH_PROVIDED` | `COUNTRY_MISMATCH_WITH_PROVIDED` | OCR — el país emisor no coincide con `expected_details.country` (o `id_country`). |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_NATIONALITY_MISMATCH_WITH_PROVIDED` | `NATIONALITY_MISMATCH_WITH_PROVIDED` | OCR — la nacionalidad extraída no coincide con `expected_details.nationality`. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_IDENTIFICATION_NUMBER_MISMATCH_WITH_PROVIDED` | `IDENTIFICATION_NUMBER_MISMATCH_WITH_PROVIDED` | OCR — el número de documento, personal o fiscal extraído no coincide con `expected_details.identification_number`. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_COULD_NOT_RECOGNIZE_DOCUMENT` | `COULD_NOT_RECOGNIZE_DOCUMENT` | El proceso de OCR devuelve un resultado vacío o irreconocible; en producción se genera COULD\_NOT\_RECOGNIZE\_DOCUMENT. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_MRZ_NOT_DETECTED` | `MRZ_NOT_DETECTED` | OCR — el documento debería tener una MRZ, pero el proceso no pudo localizarla. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_MRZ_VALIDATION_FAILED` | `MRZ_VALIDATION_FAILED` | OCR — se encontró la MRZ, pero no superó la validación de suma de control o de formato. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME` | `MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME` | OCR — los campos de identidad de la MRZ no coinciden con los campos extraídos de la VIZ. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_BARCODE_NOT_DETECTED` | `BARCODE_NOT_DETECTED` | OCR — el documento debería tener un código de barras, pero no se encontró ninguno. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_BARCODE_VALIDATION_FAILED` | `BARCODE_VALIDATION_FAILED` | OCR — se decodificó el código de barras, pero no superó la validación cruzada de campos. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_QR_NOT_DETECTED` | `QR_NOT_DETECTED` | OCR — el documento debería tener un código QR, pero no se encontró ninguno. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_QR_VALIDATION_FAILED` | `QR_VALIDATION_FAILED` | OCR — se decodificó el código QR, pero no superó la validación cruzada de campos. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_DATA_INCONSISTENT` | `DATA_INCONSISTENT` | OCR — las comprobaciones de coherencia interna de los campos de la VIZ no concuerdan. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_DOCUMENT_SIDES_MISMATCH` | `DOCUMENT_SIDES_MISMATCH` | OCR — el document\_name del anverso y el del reverso no corresponden al mismo documento. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_NAME_NOT_DETECTED` | `NAME_NOT_DETECTED` | OCR — no se pudo extraer del documento ni el nombre ni el apellido. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_DATE_OF_BIRTH_NOT_DETECTED` | `DATE_OF_BIRTH_NOT_DETECTED` | OCR — esa clase de documento debería incluir una fecha de nacimiento, pero la extracción no devolvió ninguna. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_EXPIRATION_DATE_NOT_DETECTED` | `EXPIRATION_DATE_NOT_DETECTED` | OCR — esa clase de documento debería incluir una fecha de caducidad, pero la extracción no devolvió ninguna. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_DOCUMENT_NUMBER_NOT_DETECTED` | `DOCUMENT_NUMBER_NOT_DETECTED` | OCR — no se pudo extraer ni el número de documento ni el número personal. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_COULD_NOT_DETECT_DOCUMENT_TYPE` | `COULD_NOT_DETECT_DOCUMENT_TYPE` | OCR — el clasificador no pudo asociar la captura a un tipo de documento admitido. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_PORTRAIT_IMAGE_NOT_DETECTED` | `PORTRAIT_IMAGE_NOT_DETECTED` | OCR — el proceso no pudo aislar la zona del retrato en el documento. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_UNPARSED_ADDRESS` | `UNPARSED_ADDRESS` | OCR — el documento tiene un campo de dirección, pero la geocodificación o el análisis no devolvieron nada utilizable. |
| DATABASE\_VALIDATION | `expected_details.first_name` | `SANDBOX_DB_NO_MATCH` | `DATABASE_VALIDATION_NO_MATCH` | Validación de base de datos — todos los servicios gubernamentales o de registro devuelven NO\_MATCH; la sesión se rechaza en la validación de base de datos. |
| FACEMATCH | `expected_details.first_name` | `SANDBOX_FACE_LOW_MATCH` | `LOW_FACE_MATCH_SIMILARITY` | Coincidencia facial — la puntuación de similitud está por debajo del umbral de rechazo; la sesión se rechaza en la coincidencia facial. |
| PHONE | `phone_number` | `+15005550009` | `HIGH_RISK_PHONE_NUMBER` | Teléfono — número de prueba reservado como no entregable o de alto riesgo; la simulación de la verificación de teléfono devuelve un fallo. |
| EMAIL | `user_email` | `sandbox+breach@didit.test` | `BREACHED_EMAIL_DETECTED` | Correo electrónico — la base de datos de filtraciones devuelve una coincidencia para esta dirección. |
| LOCATION | `expected_details.first_name` | `SANDBOX_IP_BLOCKLIST` | `IP_ADDRESS_IN_BLOCKLIST` | Análisis de IP — la dirección IP del solicitante aparece en la lista de bloqueo de la aplicación; la sesión se rechaza en el análisis de IP. |
| PROOF\_OF\_ADDRESS | `expected_details.first_name` | `SANDBOX_POA_ADDRESS_MISMATCH` | `ADDRESS_MISMATCH_WITH_PROVIDED` | Prueba de domicilio — la dirección del documento subido no coincide con la dirección que el cliente rellenó previamente en `expected_details`; la sesión se rechaza en la prueba de domicilio. |
| LIVENESS | `expected_details.first_name` | `SANDBOX_LIVENESS_ATTACK` | `LIVENESS_FACE_ATTACK` | Prueba de vida — la simulación de la prueba de vida pasiva detecta una señal de ataque facial; la sesión se rechaza en la prueba de vida. |
| NFC | `expected_details.first_name` | `SANDBOX_NFC_CHIP_NOT_VERIFIED` | `NFC_CHIP_NOT_VERIFIED` | NFC — la simulación de pasaporte electrónico devuelve un chip cuya firma SOD/DG no supera la comprobación de integridad; en producción se emite NFC\_CHIP\_NOT\_VERIFIED y la sesión se rechaza cuando el flujo rechaza los chips no verificados. |
| NFC | `expected_details.first_name` | `SANDBOX_NFC_TRUST_ANCHOR_MISSING` | `NFC_TRUST_ANCHOR_MISSING` | NFC — el DSC se encadena a una CSCA que no está en el almacén de confianza; en producción se emite NFC\_TRUST\_ANCHOR\_MISSING y se aplica la acción configurada para chips no verificados. |
| DOCUMENT\_AI | `expected_details.first_name` | `SANDBOX_DOCUMENT_AI_UNREADABLE` | `DOCUMENT_AI_UNREADABLE_DOCUMENT` | Document AI — la extracción no devuelve ningún campo legible; en producción se emite DOCUMENT\_AI\_UNREADABLE\_DOCUMENT y el documento queda rechazado una vez agotados los reintentos disponibles. |
| AML | `user_email` | `sandbox+aml_review@didit.test` | `POSSIBLE_MATCH_FOUND` | AML — posible coincidencia que requiere resolución manual; la sesión queda en revisión. |
| FACEMATCH | `expected_details.first_name` | `SANDBOX_FACE_BORDERLINE_MATCH` | `LOW_FACE_MATCH_SIMILARITY` | Coincidencia facial — similitud límite entre los umbrales de revisión y de rechazo; la sesión queda en revisión. |
| ID\_VERIFICATION | `expected_details.first_name` | `SANDBOX_OCR_BORDERLINE_AGE` | `MINIMUM_AGE_NOT_MET` | OCR — la fecha de nacimiento está por debajo de la edad mínima, pero el flujo envía a revisión en lugar de rechazar los fallos de edad mínima; la sesión queda en revisión. |
| DATABASE\_VALIDATION | `expected_details.first_name` | `SANDBOX_DB_PARTIAL_MATCH` | `DATABASE_VALIDATION_PARTIAL_MATCH` | Validación de base de datos — la identificación coincide, pero el nombre o la fecha de nacimiento solo coinciden parcialmente; la sesión queda en revisión. |
| PROOF\_OF\_ADDRESS | `expected_details.first_name` | `SANDBOX_POA_PARTIAL_MATCH` | `ADDRESS_MISMATCH_WITH_PROVIDED` | Prueba de domicilio — la dirección del documento subido coincide parcialmente con la dirección proporcionada; la sesión queda en revisión. |
| KYB\_REGISTRY | `expected_details.registration_number` | `SANDBOX-KYB-MISMATCH` | `KYB_REGISTRY_DATA_MISMATCH_WITH_EXPECTED` | KYB - los datos de la empresa en el registro no coinciden con los datos esperados; la sesión se rechaza. |
| KYB\_REGISTRY | `expected_details.registration_number` | `SANDBOX-KYB-REVIEW` | `KYB_REGISTRY_DATA_PARTIAL_MATCH_WITH_EXPECTED` | KYB - los datos de la empresa en el registro coinciden parcialmente; la sesión queda en revisión. |

## Consulta sin documento y wallets de identidad

Las aplicaciones de sandbox pueden habilitar todas las wallets de identidad del catálogo de países, incluidas las que no están disponibles en las aplicaciones de producción.
El escenario predeterminado `approve` simula una identidad verificada sin iniciar sesión en una wallet externa.
El resultado se guarda en la sesión y el flujo continúa con su siguiente paso normal.
El sandbox no valida credenciales reales de wallets ni cobra por los intentos simulados.

Las solicitudes de consulta siguen validando los campos introducidos y aplican el límite de intentos configurado, que de forma predeterminada es uno.
Usa los siguientes escenarios para probar los reintentos y las reglas de respaldo:

| Escenario | Resultado simulado |
| - | - |
| `lookup_partial_match` | Coincidencia parcial en el registro; cuenta para el límite de intentos. |
| `lookup_no_match` | El registro responde sin coincidencia; cuenta para el límite de intentos. |
| `lookup_provider_error` | El registro no devuelve respuesta; se pide al usuario que lo intente de nuevo y, tras tres intentos sin respuesta, se aplica el respaldo por error del proveedor. |
| `wallet_cancelled` | El usuario cancela el inicio de sesión en la wallet. |
| `wallet_timeout` | El inicio de sesión en la wallet expira. |
| `wallet_provider_error` | La wallet no puede completar el inicio de sesión. |

Los fallos de wallet siguen la configuración del país para pasar a la captura de documento o rechazar.
Los intentos de consulta completados se registran por separado, de modo que dos intentos permitidos producen dos resultados antes del respaldo.
Todo el uso del sandbox sigue siendo gratuito.

## Verificación de cuenta de Verificación bancaria

La Verificación bancaria aún no está habilitada en producción, así que esta sección se aplica cuando lo esté: hasta entonces, una clave de sandbox recibe la misma respuesta `bank_verification_not_available` que una clave de producción.

Un paso de Verificación bancaria en sandbox ofrece [ambos métodos](https://docs.didit.me/es/core-technology/bank-verification/verification-methods.md) en todos los países, en el orden que define el paso, en un servicio simulado sin banco y sin coste.
Con el orden predeterminado, la persona empieza en el inicio de sesión bancario y elige **Introduce los datos de tu cuenta** para ejecutar la [verificación de cuenta](https://docs.didit.me/es/core-technology/bank-verification/account-check.md); define `bank_method_order` como `["account_lookup", "open_banking"]` para empezar por ella.
Los cuatro últimos caracteres de la cuenta introducida determinan la respuesta; consulta [Pruébalo en sandbox](https://docs.didit.me/es/core-technology/bank-verification/account-check.md#pruébalo-en-sandbox) para ver todas las terminaciones y una cuenta válida por formato.
La [API de verificación de cuenta](https://docs.didit.me/es/standalone-apis/bank-account-check.md) devuelve las respuestas correspondientes para una clave de sandbox según las mismas terminaciones.

## Los documentos de muestra

> [!WARNING]
>
> Nunca subas documentos de identidad reales en sandbox. Usa en su lugar los documentos de muestra integrados.

El flujo alojado ofrece tres documentos de muestra, uno por tipo de documento:

| País | Tipo de documento |
| - | - |
| España | Documento nacional de identidad |
| Argentina | Pasaporte |
| Polonia | Licencia de conducir |

Toca una muestra en la tira para usarla en lugar de una captura real. Los datos extraídos y el resultado de la sesión siempre provienen del escenario o del valor mágico seleccionado, así que cualquier muestra funciona con cualquier escenario.

## Identidad de muestra: pruebas de `expected_details`

Cada documento de sandbox se genera a partir de una única identidad **fija**.
Los valores extraídos son los mismos en todas las sesiones y nunca se derivan de la fecha actual, así que puedes rellenar `expected_details` con ellos y comprobar de principio a fin tu comparación de campos y tu gestión de advertencias.
Si envías los valores siguientes, las comprobaciones de comparación se superan; si envías cualquier otro, se activa a propósito la advertencia `*_MISMATCH_WITH_PROVIDED` correspondiente.

Las fechas son comunes a todos los documentos de muestra:

| Campo | Valor |
| - | - |
| `date_of_birth` | `1990-06-15` |
| Fecha de expedición | `2025-06-15` |
| Fecha de caducidad | `2035-06-15` |

El resto de la identidad depende del documento que uses:

| `sandbox_sample` | Documento | `first_name` | `last_name` | `gender` | `nationality` | `id_country` | `identification_number` |
| - | - | - | - | - | - | - | - |
| *(predeterminado, sin muestra seleccionada)* | Pasaporte electrónico de Brasil | `GUSTAVO` | `MATSUMOTO` | `M` | `BRA` | `BRA` | `YC531181` |
| `spain-id-card-2021` | Documento nacional de identidad de España | `CARMEN` | `ESPAÑOLA ESPAÑOLA` | `F` | `ESP` | `ESP` | `CAA000000` |
| `argentina-passport-2009` | Pasaporte de Argentina | `MARIA VICTORIA` | `VILLAREAL` | `F` | `ARG` | `ARG` | `AAF794880` |
| `poland-driving-license-2019` | Licencia de conducir de Polonia | `JAN` | `KOWALSKI` | no figura en este documento | `POL` | `POL` | `SP005/89/5` |

```json theme={null}
{
  "workflow_id": "<your-workflow-uuid>",
  "expected_details": {
    "date_of_birth": "1990-06-15",
    "gender": "M",
    "nationality": "BRA",
    "identification_number": "YC531181"
  }
}
```

`first_name` y `last_name` son la excepción: el sandbox replica en el documento generado los nombres que envías en `expected_details`, así que la coincidencia de nombres pasa con cualquier nombre.
Usa el valor mágico `SANDBOX_OCR_FULL_NAME_MISMATCH_WITH_PROVIDED` para probar una discrepancia de nombre, y `SANDBOX_OCR_DOB_MISMATCH_WITH_PROVIDED` para una discrepancia de fecha de nacimiento.

> [!NOTE]
>
> Si tu flujo restringe `documents_allowed` a un país o tipo de documento que excluye el pasaporte electrónico de Brasil, el sandbox genera en su lugar un documento del primer país permitido, de modo que `nationality`, `id_country` y el nombre del documento corresponden a ese país. Las fechas anteriores no cambian.

### Qué no incluye el documento generado

El documento generado es la plantilla genérica del país y tipo de documento permitidos, así que los campos que solo aporta un documento real emitido por el Estado quedan vacíos. El que importa para el enrutamiento es la **región / estado de EE. UU.** (`kyc.region`): una licencia de conducir o un documento de identidad de EE. UU. en sandbox no tiene estado, así que una condición de rama como `Region equals IL` nunca puede cumplirse en sandbox y, cuando tampoco se cumple ninguna otra rama de ese nodo, el flujo toma la rama de respaldo del nodo (la rama sin condiciones). No se trata de una revisión ni de un fallo de la rama: la condición simplemente no tiene nada que leer. Un campo ausente también falla con `not equals` y `not in`; consulta [cómo leen las condiciones de rama los datos de la sesión](https://docs.didit.me/es/console/workflows.md#cómo-leen-las-condiciones-de-rama-los-datos-de-la-sesión) para ver la diferencia entre un campo ausente y una cadena vacía.

Para probar en sandbox un flujo que depende del estado, basa la decisión en un valor de la sesión que controles tú, en lugar de en el documento:

* Envía el estado en `metadata` al crear la sesión (por ejemplo, `"metadata": {"credential_state": "IL"}`) y establece una condición de rama basada en `metadata.credential_state`. Los metadatos se evalúan en sandbox exactamente igual que en producción.
* Mantén la condición `kyc.region` basada en el documento para el tráfico de producción si también necesitas que el documento acredite el estado; en modo de producción, la región se lee del documento real.

Todo lo demás del enrutamiento es idéntico a producción: el grafo del flujo, las condiciones de rama, los valores de `metadata` y `expected_details`, los nodos de acción y los nodos de estado se ejecutan de verdad. El sandbox nunca envía por sí solo una sesión a revisión: una sesión de sandbox termina en revisión solo cuando el flujo la dirige allí (un escenario `review_*`, un valor mágico asociado a una acción de revisión o un nodo de estado en el grafo).

## Códigos OTP y activación de correos electrónicos

* **OTP por teléfono:** la comprobación telefónica de sandbox acepta cualquier código con formato válido (de 4 a 8 dígitos; `123456` incluido). Una entrada con formato incorrecto sigue devolviendo `400`.
* **OTP por correo electrónico:** la comprobación de correo electrónico de sandbox acepta cualquier código con formato válido (`123456` incluido). Una entrada con formato incorrecto sigue devolviendo `400`.

De forma predeterminada, las sesiones de sandbox **no** envían correos electrónicos (alertas de revisión, cambio de estado, OTP, reenvío, enlace de nueva sesión, etc.). Para enviar un correo electrónico de todos modos, usa uno de estos patrones de activación en la dirección del destinatario:

| Patrón de activación | Ejemplo |
| - | - |
| `<local>+sandbox@<domain>` | `alice+sandbox@example.com` |
| `*@didit.test` | `alice@didit.test` |

## Controlar resultados: escenarios, valores mágicos, armado y el endpoint de simulación

Hay cinco formas de controlar el resultado en sandbox.

### 1. `sandbox_scenario` al crear la sesión

Indica el slug del escenario al crear la sesión (`POST /v3/session/`). El proceso de creación expande los valores mágicos incluidos en el escenario en los campos de entrada de la sesión, de modo que los proveedores simulados emiten el resultado previsto a través del pipeline real del flujo.

```json theme={null}
{
  "workflow_id": "<your-workflow-uuid>",
  "sandbox_scenario": "decline_document_expired"
}
```

### 2. Valores mágicos introducidos a mano

Define tú mismo los campos de entrada individuales (por ejemplo, `expected_details.first_name = "SANDBOX_OCR_DOCUMENT_EXPIRED"` o `user_email = "sandbox+aml_hit@didit.test"`). Consulta la tabla de valores mágicos anterior para ver el registro completo.

### 3. Armar (o volver a armar) la sesión antes de que empiece el flujo

Define o sustituye el escenario armado en una sesión que ya existe, siempre que no haya avanzado más allá de `Not Started`. Este es el endpoint que invoca el [selector de escenarios previo al flujo](#sandbox-en-el-flujo-de-verificación-alojado), y puedes invocarlo tú mismo para crear un selector personalizado o un entorno de pruebas:

```
POST /v3/session/{session_id}/sandbox/arm/
Session-Token: <session_token>
```

```json theme={null}
{
  "scenario": "decline_document_expired"
}
```

Respuesta 200:

```json theme={null}
{
  "session_id": "<session_id>",
  "scenario": "decline_document_expired",
  "status": "Not Started"
}
```

* Autentícate con el propio `Session-Token` de la sesión (devuelto como `session_token` al crear la sesión), no con la clave de API de tu aplicación: se trata de un endpoint orientado al cliente.
* Devuelve `400` para un slug de escenario desconocido, y `400` cuando la sesión ya ha avanzado más allá de `Not Started` (`"Scenario can only be armed before the verification starts."`).
* Devuelve `404` cuando el `Session-Token` pertenece a una aplicación de producción (no sandbox) o no coincide con el `session_id` de la ruta.
* **Volver a armar sustituye el escenario anterior.** Solo borra los valores mágicos que escribió el escenario anterior y deja intacto todo lo que el usuario ya haya introducido a mano (correo electrónico, nombre, etc.). Por ejemplo: arma `decline_phone_high_risk` y luego arma `approve`: el número de teléfono vuelve a quedar sin definir, pero un correo electrónico introducido por el cliente se conserva tras ambas llamadas.

### 4. El endpoint de simulación

Fuerza un estado final a posteriori:

* Consola / clave de API (requiere el privilegio `write:sessions`; una clave de API con alcance limitado necesita acceso de escritura a las sesiones): `POST /v3/session/{session_id}/simulate/`
* Flujo alojado (encabezado `Session-Token`): `POST /v3/session/{session_id}/sandbox/simulate/`

Ambos funcionan con sesiones sandbox de KYC y KYB.
Una sesión de producción (no sandbox) devuelve `404`, al igual que un `Session-Token` que no coincide con el `session_id` de la ruta.

Todos los campos del cuerpo son opcionales, pero se requiere al menos uno de `new_status`, `scenario` o `risk`:

| Campo | Tipo | Qué hace |
| - | - | - |
| `new_status` | string | `Approved`, `Declined`, `In Review`, `Expired` o `Abandoned`. Si lo omites, toma por defecto el estado predeterminado del caso de riesgo (`risk`, o el primer riesgo que incluyen los valores mágicos del escenario) y, si no lo hay, el estado predeterminado del escenario. |
| `scenario` | string, hasta 100 caracteres | Un slug de escenario. Sin `risk`, se aplica el primer riesgo que incluyen sus valores mágicos. |
| `risk` | string, hasta 128 caracteres | Un caso de riesgo. Su advertencia se añade a la sesión. |
| `node_id` | string, hasta 128 caracteres | El nodo del flujo al que se asocia la advertencia. Por defecto, el nodo actual de la sesión. |
| `comment` | string, hasta 500 caracteres | Nota que se añade a la entrada de actividad que registra el cambio de estado. |
| `send_webhooks` | boolean, `true` por defecto | Indica si el cambio de estado envía webhooks. |

`send_webhooks: true` envía exactamente los mismos webhooks que un cambio de estado real: `status.updated`, más los eventos `user.status.updated` y `user.data.updated` (o `business.status.updated` y `business.data.updated` en una sesión de empresa) que provoca el cambio, a los destinos suscritos a ellos.
Con `send_webhooks: false` el estado cambia igualmente, pero no se registra ni se envía ningún webhook.
La recuperación de webhooks perdidos nunca reenvía esa decisión, y la consola se sigue actualizando en tiempo real.
El siguiente cambio de estado de la sesión envía webhooks con normalidad.

```json theme={null}
{
  "scenario": "decline_aml_hit",
  "send_webhooks": false
}
```

Respuesta 200:

```json theme={null}
{
  "session_id": "<session_id>",
  "previous_status": "In Progress",
  "status": "Declined",
  "scenario": "decline_aml_hit",
  "risk": "POSSIBLE_MATCH_FOUND",
  "send_webhooks": false
}
```

* `status` es el estado que fijó la simulación, y `previous_status` el que tenía la sesión antes.
* `scenario` devuelve tu slug y `risk` es el caso de riesgo aplicado; cada uno es `null` cuando no hay ninguno.
* `send_webhooks` devuelve el valor aplicado.
* Devuelve `400` para un escenario o riesgo desconocido, cuando no se puede deducir ningún `new_status`, para cualquier otro `new_status` y cuando la sesión ya tiene el estado solicitado (`["The new status is the same as the current status."]`).

### 5. `sandbox_scenario` en la API de AML independiente

El análisis AML desde el back-end no tiene usuario final ni sesión que armar, así que el slug va en la propia solicitud de análisis (`POST /v3/aml/`):

```json theme={null}
{
  "full_name": "Ada Lovelace",
  "save_api_request": true,
  "sandbox_scenario": "decline_aml_hit"
}
```

* `decline_aml_hit` devuelve una coincidencia con el formato de producción y resuelve el análisis como `Declined`; `review_aml_possible_match` lo resuelve como `In Review`. Un escenario que no arma ningún resultado AML deja el análisis limpio.
* Con `save_api_request: true` (el valor por defecto), el análisis se guarda exactamente igual que con una clave de producción, de modo que el `request_id` devuelto se resuelve mediante `GET /v3/session/{request_id}/decision/` y `GET /v3/session/{request_id}/generate-pdf/`.
* Devuelve `400` para un slug de escenario desconocido, y `400` cuando la clave de API pertenece a una aplicación de producción.

## Dónde aparece `sandbox_scenario`

El slug del escenario armado (`null` si no hay ninguno activado) se devuelve en algunas ubicaciones y se omite intencionadamente en otras:

| Ubicación | Se expone |
| - | - |
| Payload del paso actual (KYC y KYB) | Sí, como `sandbox_scenario` |
| `GET /v1/session/{id}/decision/` | Sí, como `sandbox_scenario` |
| `GET /v3/session/{id}/decision/` (consulta [Recuperar sesión](https://docs.didit.me/es/sessions-api/retrieve-session.md)) | Sí, `sandbox_scenario` en el nivel superior (null cuando no está activado) |
| Webhooks de sesión (`status.updated`, `data.updated`) | Sí, como `sandbox_scenario`, `null` en sesiones de producción o en sesiones sandbox sin activar |

Si tu integración necesita aplicar lógica condicional en función del escenario activado del lado del servidor, consúltalo en el payload del webhook o de cualquiera de los dos endpoints de decisión (`v1` o `v3`).

## Uso y facturación

El uso en sandbox se registra, pero nunca se cobra:

* Cada llamada simulada sigue registrando un evento de uso (`is_sandbox: true`, con precio `0.0000`) para que el volumen de sandbox sea visible al depurar, pero nunca se suma a tus totales de uso, nunca se descuenta del saldo y nunca aparece en una factura.
* Las secciones de saldo, recarga y uso en producción de la Business Console excluyen por defecto el uso en sandbox. Los endpoints de uso de recargas aceptan un parámetro `environment` (`live`, el valor por defecto, o `sandbox`) para que puedas consultar el volumen de sandbox por separado, siempre sin coste y sin consumir el plan gratuito.
* La verificación de correo electrónico y la verificación de teléfono se facturan por solicitud y no según el resultado de la sesión, y sandbox no puede reproducir eso por completo en la capa de webhooks. Las comprobaciones de correo electrónico y teléfono en sandbox siguen registrando un evento de uso por solicitud con precio `0.0000`, igual que cualquier otra función simulada: es una divergencia conocida y aceptada respecto a la facturación en producción, no un error.
* La estructura de la respuesta de la validación de base de datos en sandbox difiere intencionadamente de la estructura por proveedor de producción, ya que no hay ningún proveedor real que reproducir. Considera los payloads de validación de base de datos en sandbox como una representación del resultado (estado y código de advertencia), no como una vista previa exacta, byte a byte, de la respuesta de un proveedor en producción.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.