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

# Informes Qscore de fraude e identidad

> Genera una evaluación explicable de fraude e identidad con idempotencia, PDF, webhook y verificación pública de autenticidad.

export const EnvUrls = ({lang = "en"}) => {
  const T = {
    en: {
      test: "Test",
      live: "Live",
      hint: "Same API on both environments — build against test first, then go live by swapping the base URL and the key.",
      guide: "Environments and testing",
      href: "/en/environment-testing"
    },
    es: {
      test: "Test",
      live: "Live",
      hint: "La misma API en ambos ambientes — construye primero contra test y pasa a live cambiando la URL base y la key.",
      guide: "Entorno y pruebas",
      href: "/es/environment-testing"
    },
    zh: {
      test: "Test",
      live: "Live",
      hint: "两个环境的 API 完全一致——先在 test 环境构建，再通过切换基础 URL 和密钥上线。",
      guide: "环境与测试",
      href: "/zh/environment-testing"
    }
  };
  const t = T[lang] || T.en;
  const row = (label, url, keyPattern, badgeCls) => <div className="flex flex-wrap items-center gap-2 px-3 py-2">
      <span className={"rounded px-1.5 py-0.5 text-xs font-semibold uppercase tracking-wide " + badgeCls}>
        {label}
      </span>
      <code className="text-xs">{url}</code>
      <span className="text-xs text-gray-400 dark:text-zinc-500">·</span>
      <code className="text-xs">{keyPattern}</code>
    </div>;
  return <div className="my-4 rounded-xl border border-gray-200 dark:border-zinc-700 divide-y divide-gray-200 dark:divide-zinc-700 text-sm not-prose">
      {row(t.test, "https://cryptobank.qbank.cl/platform", "pk_test_...", "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300")}
      {row(t.live, "https://api.qbank.cl/platform", "pk_...", "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300")}
      <p className="px-3 py-2 text-xs text-gray-500 dark:text-zinc-400 m-0">
        {t.hint} <a href={t.href}>{t.guide} →</a>
      </p>
    </div>;
};

<EnvUrls lang="es" />

Fraude e identidad es un producto separado del informe de crédito. Devuelve
un **score de riesgo de fraude**: un valor más alto significa mayor riesgo
observado. Combina velocidad de consultas, señales verificadas de la
plataforma, consistencia de contacto, evidencia de dispositivo/IP compartido
y cobertura de buró disponible. No es un score crediticio.

<Note>
  El producto exige el service flag `risk` y cobra el servicio fijo
  `risk_fraud_score`. Si falla la generación, el cargo se reembolsa
  automáticamente. El PDF y las señales detalladas nunca viajan por email.
</Note>

## Flujo

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
sequenceDiagram
    participant C as Tu sistema
    participant P as Plataforma CBPay
    participant Q as Capacidad de buró
    C->>P: POST /v1/qscore/fraud/reports
    P->>P: Valida purpose, idempotencia y ownership
    P->>Q: Lee registros normalizados
    alt Fuente personal disponible
        Q-->>P: Registros frescos (o cero registros)
        P->>P: Marca fuente live; agrega THIN_FILE si no hay registros
    else No hay fuente personal configurada
        Q-->>P: 503 bureau_unavailable
        P->>P: Continúa con THIN_FILE y señales internas
    else Falla de fetch o contrato
        Q-->>P: sources_failed / transporte / contrato inválido
        P->>P: Falla cerrado; recovery/reembolso
    end
    P->>P: Calcula qscore-fraud-v1 y genera PDF
    P-->>C: 200 informe (ready o failed)
    P-->>C: Webhook risk_fraud_score_ready
    C->>P: GET /pdf autenticado
```

## Cobertura del buró y `THIN_FILE`

El informe puede quedar `ready` con `THIN_FILE` cuando no tiene evidencia
utilizable del buró para esa evaluación. Esto puede ocurrir después de un
fetch exitoso que devuelve cero registros, incluido un HTTP `200` con
`sources_queried: 0`. Esto significa que no hubo una fuente personal
consultable: la respuesta solo contenía fuentes bulk o no compatibles. Se
trata como `unavailable` y puede producir `THIN_FILE`. Lo mismo ocurre cuando
el core responde explícitamente `503 bureau_unavailable` porque no hay una
fuente personal de buró configurada. En ese caso, el PDF declara la fuente
personal como `unavailable` con cero registros; las señales de la plataforma
todavía pueden contribuir al score de fraude.

Cuando `sources_queried > 0`, la fuente personal respondió la consulta pero no
devolvió evidencia para el sujeto; sigue siendo un fetch válido y también puede
producir `THIN_FILE`.

`THIN_FILE` describe cobertura de evidencia; no significa limpio, aprobado ni
bajo riesgo. La tabla `sources` del PDF declara cada fuente funcional, su
cantidad de registros y `freshness` (`live` o `unavailable`). Los registros
persistidos del buró nunca se presentan como evidencia de identidad fresca
cuando el fetch on-demand no estuvo disponible.

Excepción para registros anteriores: los registros `Source: open_finance` derivados de consent
links pueden no traer `ingest_run_id`. No cuentan como evidencia personal fresca
del buró, no deben marcar `FetchedThisRun` y no representan una fuente personal
de fraude.

Las fallas son fail-closed: un `sources_failed` no vacío, una falla de
transporte o autenticación, una falla de storage o un contrato inválido no
producen un éxito de archivo delgado. El recovery/reembolso existente resuelve
el informe cobrado.

## Crear un informe

`POST /v1/qscore/fraud/reports` es autenticado y exige una cuenta verificada.
La clave puede ir en el body o en el header `Idempotency-Key`.

| Campo | Obligatorio | Descripción |
| - | -: | - |
| `country` | sí | País ISO 3166-1 alpha-2 del documento. |
| `doc_id` | sí | Documento del sujeto; el RUT chileno se normaliza y valida. |
| `subject_type` | no | `person` o `company`; se infiere si es posible. |
| `purpose` | sí | `fraud_prevention`, `identity_verification`, `onboarding` u `other`. |
| `lang` | no | `en`, `es` o `zh`; default `en`. |
| `idempotency_key` | sí | Clave única de esta compra. |

```bash Crear informe de fraude theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "https://api.qbank.cl/platform/v1/qscore/fraud/reports" \
  -H "Authorization: Bearer pk_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: fraud-check-2026-09-06-001" \
  -d '{"country":"CL","doc_id":"76.123.456-0","subject_type":"company","purpose":"identity_verification","lang":"es"}'
```

```json 200 OK theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "report_id": "9f1c2d3e-4a5b-4c6d-8e7f-0a1b2c3d4e5f",
  "kind": "qscore_fraud",
  "subject_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "country": "CL",
  "doc_id": "76123456-0",
  "subject_type": "company",
  "purpose": "identity_verification",
  "status": "ready",
  "model_version": "qscore-fraud-v1",
  "lang": "es",
  "score": 280,
  "band": "B",
  "reason_codes": [{"code":"NEW_SUBJECT","direction":"negative","weight":"high"}],
  "verify_code": "F9f1c2d3e4a5b6c7d8e9f00112233445566778899aabbccddeeff0011223344",
  "verify_url": "https://business.cbpayapp.com/verify/qscore-fraud/F9f1c2d3e4a5b6c7d8e9f00112233445566778899aabbccddeeff0011223344",
  "pdf_url": "/v1/qscore/fraud/reports/9f1c2d3e-4a5b-4c6d-8e7f-0a1b2c3d4e5f/pdf",
  "created_at": "2026-09-06T15:04:12Z"
}
```

Repetir la misma clave devuelve el informe original con
`"idempotency_hit": true`; no cobra ni genera otro informe.

## Consultar y descargar

`GET /v1/qscore/fraud/reports` exige `from` y `to` (`YYYY-MM-DD`, timezone de
la organización, inclusivos), acepta `status` y pagina con `page`/
`page_size` (default 50, máximo 200). `GET .../{report_id}` devuelve sus
metadatos, razones y links. Un ID inválido o fuera de ownership responde
`404 not_found`.

`GET /v1/qscore/fraud/reports/{report_id}/pdf` descarga el PDF autenticado
como `qxrisk_fraud_<report_id>.pdf`. Solo existe cuando el estado es `ready`.

```bash Listar informes listos theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.qbank.cl/platform/v1/qscore/fraud/reports?from=2026-09-01&to=2026-09-30&status=ready&page=1&page_size=50" \
  -H "Authorization: Bearer pk_live_..."
```

```json 200 OK theme={"theme":{"light":"github-light","dark":"github-dark"}}
{"items":[{"report_id":"9f1c2d3e-4a5b-4c6d-8e7f-0a1b2c3d4e5f","kind":"qscore_fraud","country":"CL","doc_id":"76123456-0","purpose":"identity_verification","status":"ready","score":280,"band":"B","created_at":"2026-09-06T15:04:12Z"}],"meta":{"page":1,"page_size":50,"total":1}}
```

## Score, bandas y estados

El modelo es `qscore-fraud-v1`, de 1 a 999. A diferencia del crédito, aquí
un score alto es peor.

| Banda | Score | Lectura |
| - | -: | - |
| `A` | 1–199 | Menor riesgo observado |
| `B` | 200–399 | Riesgo bajo a moderado |
| `C` | 400–599 | Riesgo moderado |
| `D` | 600–799 | Riesgo alto |
| `E` | 800–999 | Riesgo muy alto |

Los reason codes implementados son `NEW_SUBJECT`, `VELOCITY`,
`CONTACT_MISMATCH`, `SHARED_DEVICE_IP` y `THIN_FILE`.

| Estado | Significado | Acción |
| - | - | - |
| `pending` | Registro creado mientras genera | Consultar el detalle |
| `ready` | Score, razones, código y PDF disponibles | Leer o descargar |
| `failed` | Falló la generación y se reembolsó | Leer `error_code` y reintentar con otra clave |

## Webhook, email y verificación pública

El webhook firmado `risk_fraud_score_ready` llega a la cuenta:

```json risk_fraud_score_ready theme={"theme":{"light":"github-light","dark":"github-dark"}}
{"report_id":"9f1c2d3e-4a5b-4c6d-8e7f-0a1b2c3d4e5f","kind":"qscore_fraud","subject_id":"3fa85f64-5717-4562-b3fc-2c963f66afa6","country":"CL","doc_id":"76••••••-0","score":280,"band":"B","status":"ready"}
```

El email es best-effort y brandeado. Solo contiene una referencia corta, el
documento consultado y acceso a la cuenta: no incluye PDF, score, banda ni
señales.

`GET /verify/qscore-fraud/{code}` no requiere auth. Un código válido devuelve:

```json 200 OK theme={"theme":{"light":"github-light","dark":"github-dark"}}
{"valid":true,"type":"qscore_fraud","status":"ready","date":"2026-09-06","issued_by":"CBPay"}
```

Un código inválido responde `404` con `valid:false` y nunca revela PII,
score ni señales. El endpoint está limitado por IP.

## Errores

| HTTP | Código | Solución |
| -: | - | - |
| 400 | `invalid_doc_id` | Corrige el documento para el país |
| 400 | `invalid_purpose` | Usa uno de los cuatro propósitos cerrados |
| 400 | `invalid_subject_type` | Envía `person` o `company` |
| 400 | `idempotency_key_required` | Envía el campo o el header |
| 403 | `verification_required` | Completa KYC/KYB |
| 403 | `service_disabled` | Habilita `risk` para la cuenta |
| 404 | `not_found` | Revisa el ID o espera que el PDF esté listo |
| 429 | `too_many_attempts` | Reduce la frecuencia de verificaciones públicas |
| 502 | `generation_failed` | El cargo fue reembolsado; reintenta con clave nueva |

## FAQ

<AccordionGroup>
  <Accordion title="¿Es un score de crédito?">
    No. Es un score separado de fraude e identidad: un valor alto significa
    mayor riesgo observado.
  </Accordion>

  <Accordion title="¿Existe un endpoint score-only?">
    No. El producto siempre crea el informe completo, PDF y código verificable.
  </Accordion>

  <Accordion title="¿Un retry cobra dos veces?">
    No si reutilizas la misma clave de idempotencia.
  </Accordion>
</AccordionGroup>


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