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

# Formato de respuesta

> El envelope {data, timestamp, status} y la forma de data en /analyze

Toda respuesta exitosa de la API viaja envuelta en el mismo formato:

```json theme={null}
{
  "data": { },
  "timestamp": "2026-07-29T16:43:36.22",
  "status": "OK"
}
```

| Campo       | Descripción                                                         |
| ----------- | ------------------------------------------------------------------- |
| `data`      | El resultado propio del endpoint. Abajo está la de `POST /analyze`. |
| `timestamp` | Momento en que se generó la respuesta                               |
| `status`    | `OK` en lecturas y scoring; `CREATED` al crear una verificación     |

Los errores usan otro envelope. Ver [Códigos de error](/referencia/codigos-de-error).

## `data` en `POST /analyze`

```json theme={null}
{
  "transaction_id": "01e6efd4-c884-5bc0-af43-68b3fac8d0f8",
  "order_id": "8788426783",
  "risk_score": 29.07,
  "risk_level": "medium",
  "flags": [
    {
      "code": "AUTH_3DS_FAILED",
      "category": "authentication",
      "severity": "high",
      "description": "3DS authentication explicitly failed"
    }
  ],
  "recommendation": "…",
  "decision": {
    "type": "BLOCK",
    "reason": ["AUTH_3DS_FAILED", "custom_rule:abc123"],
    "confidence": 0.89,
    "conditions": []
  },
  "transaction_state": "BLOCKED"
}
```

| Campo                 | Descripción                                                                                               |
| --------------------- | --------------------------------------------------------------------------------------------------------- |
| `transaction_id`      | Identificador que asigna FraudEG                                                                          |
| `order_id`            | Identificador de orden que enviaste                                                                       |
| `risk_score`          | Número de 0 a 100 — campo **plano**. Ver [Score de riesgo](/conceptos/score-y-decision)                   |
| `risk_level`          | `low` / `medium` / `high` / `critical` — campo **plano**                                                  |
| `flags`               | Señales de riesgo con `code`, `category`, `severity`, `description`. Catálogo: [Flags](/referencia/flags) |
| `recommendation`      | Frase en el idioma de la compañía. Ver abajo                                                              |
| `decision.type`       | La decisión — [Decisiones de transacción](/conceptos/decisiones)                                          |
| `decision.reason`     | Lista de strings — ver abajo                                                                              |
| `decision.confidence` | 0 a 1, de la decisión **por score**. Ver [Score de riesgo](/conceptos/score-y-decision)                   |
| `decision.conditions` | Condiciones pendientes cuando `type` es `STEP_UP_AUTH` (hoy: `OTP_VERIFICATION`)                          |
| `transaction_state`   | [Ciclo de vida](/conceptos/ciclo-de-vida)                                                                 |

<Note>
  `risk_score` y `risk_level` son de primer nivel de `data`. `flags` también. No hay un objeto
  `risk` anidado, y `flags` no vive dentro de `decision`.
</Note>

## `recommendation`

Es un **string de prosa**, en el idioma configurado para tu compañía. Resume la decisión y,
cuando hay señales, puede citar códigos públicos de [`flags`](/referencia/flags).

No es un contrato programable:

* No hagas `switch` sobre el texto.
* No parsees los códigos desde la frase: ya están en `flags` y en `decision.reason`.
* El texto puede cambiar de redacción sin aviso; `decision.type` no.

Úsalo para mostrar algo legible en una UI. Para decidir qué hacer con la transacción, lee
siempre `decision.type`.

## Sobre `flags`

Cada entrada es una señal que se activó, con cuatro campos. `flags` viene `[]` si no hay
señales. Las entradas están deduplicadas por `code`.

Nunca traen el nombre interno de la señal ni un peso numérico.

El catálogo completo de `code` —un solo lugar, no se duplica en otras páginas— está en
[Catálogo de flags](/referencia/flags).

## Sobre `decision.reason`

Lista de **strings**, no de objetos. Lectura corta para ramificar sin recorrer `flags`:

| Forma                                          | Qué significa                                                                                                                                                                                        |
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Un código público (p. ej. `"AUTH_3DS_FAILED"`) | Se activó esa señal. Mismo vocabulario que `flags[].code`                                                                                                                                            |
| `custom_rule:<id>`                             | Una **regla propia del comercio** elevó la decisión                                                                                                                                                  |
| `entity_block:<kind>:<value>`                  | Una entrada de tu **lista de entidades** bloqueó la transacción. `<kind>` es el tipo (`user_id`, `device_id`, `ip_address`, `pan_token`, `email`, `expanded_bin`) y `<value>` el valor que coincidió |

Si te llega un `BLOCK` con `entity_block:user_id:user-4471`, la causa está en tu lista.
