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

# Refresh Intents

> Sync on-demand de un link, con soporte para MFA async.

## Por qué existen

Rail corre syncs automáticos cada 4h por cada link. Pero a veces el cliente quiere forzar un sync **ya** — por ejemplo cuando el user abre la app y quiere ver lo más reciente.

Un **refresh intent** representa ese deseo de sync on-demand. Su ID es `ri_` + 16 chars hex.

```json theme={null}
{
  "object": "refresh_intent",
  "id": "ri_a1b2c3d4e5f6a7b8",
  "link_id": "link_8a2f4c6e1b9d5037",
  "status": "succeeded",
  "refresh_type": "full",
  "requires_mfa": null,
  "created_at": "2026-06-03T15:00:00Z",
  "started_at": "2026-06-03T15:00:01Z",
  "completed_at": "2026-06-03T15:00:45Z",
  "error_code": null
}
```

## Crear

```bash theme={null}
curl https://api.rail.cl/v1/refresh_intents \
  -X POST \
  -H "Authorization: Bearer rail_sk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "link_id": "link_xxx",
    "refresh_type": "full"
  }'
```

`refresh_type`:

* `full` — todas las cuentas, todos los movs (default).
* `historical` — incluye movs viejos (60+ días).
* `only_last` — solo cuentas (skip movs). Más rápido.

## Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> created
    created --> in_progress
    in_progress --> requires_mfa: MFA requerido
    in_progress --> succeeded
    in_progress --> failed
    requires_mfa --> in_progress: User resuelve MFA en widget
    requires_mfa --> failed: Timeout o user abandona
    succeeded --> [*]
    failed --> [*]
```

| `status`       | Significa                      |
| -------------- | ------------------------------ |
| `created`      | Encolado. Aún no empezó.       |
| `in_progress`  | El worker está syncando.       |
| `requires_mfa` | El banco pidió MFA. Ver abajo. |
| `succeeded`    | Sync terminado OK.             |
| `failed`       | Sync falló. Ver `error_code`.  |

## MFA async

Algunos bancos (Santander, BCI a veces) piden MFA en cada sync. Cuando eso pasa:

1. El intent queda en `status: requires_mfa`.
2. El campo `requires_mfa.widget_token` contiene un `ri_{intentId}_sec_…` listo para abrir el widget.
3. Tu frontend abre el widget con ese token.
4. User resuelve el MFA dentro del widget.
5. El widget cierra, el sync continúa server-side.
6. El intent pasa a `succeeded` o `failed`.

```json theme={null}
{
  "object": "refresh_intent",
  "id": "ri_xxx",
  "status": "requires_mfa",
  "requires_mfa": {
    "widget_token": "ri_xxx_sec_abc123…",
    "expires_at": "2026-06-03T15:10:00Z"
  }
}
```

## Polling vs Webhooks

Dos formas de saber cuándo termina:

### Polling

```bash theme={null}
curl https://api.rail.cl/v1/refresh_intents/ri_xxx \
  -H "Authorization: Bearer rail_sk_live_…"
```

Cada 5-10 segundos hasta que `status` sea `succeeded`, `failed` o `requires_mfa`.

### Webhooks (recomendado)

Suscríbete a los eventos `refresh_intent.succeeded`, `refresh_intent.failed`, `refresh_intent.requires_mfa`. Ver [Guías → Webhooks](/guides/webhooks).

## Cooldown y deduplicación

* **Cooldown 5min**: si pediste un intent para un link, no puedes pedir otro full hasta 5min después. Para `only_last` no hay cooldown.
* **Unique in-progress**: solo puede haber 1 intent activo por link a la vez. Si pides otro, te devolvemos el existente.

```bash theme={null}
# Si ya hay uno in_progress, devuelve ese (no crea otro)
curl https://api.rail.cl/v1/refresh_intents \
  -X POST \
  -d '{"link_id": "link_xxx", "refresh_type": "full"}'
# → 200 con el intent activo, no 201
```

## Error codes comunes

| `error_code`                | Significa                                                             |
| --------------------------- | --------------------------------------------------------------------- |
| `bank_unavailable`          | El banco está caído o muy lento. Reintentar en 10min.                 |
| `rejected_credentials`      | Clave o RUT inválido. El user debe re-conectar.                       |
| `requires_mfa_user_timeout` | Se emitió `wt_*` pero el user no resolvió en 10min.                   |
| `bank_format_change`        | El banco cambió su HTML y nuestro parser rompió. Rail está sobre eso. |
