# Lista de migración

Qué revisar en tu integración tras los ajustes del contrato v1.

Si integraste antes del 5 de noviembre de 2026, revisa esta lista una vez. Ninguna fila quita un campo ni una ruta, pero varias cambian una respuesta que tu código pudo haber asumido. El detalle de cada cambio está en [Cambios](/docs/cambios/).

## Beneficiarios y `latam`

| Antes | Ahora | Qué hacer |
|---|---|---|
| Un alta `latam` inválida quedaba en `202 processing` | Responde `422 invalid_beneficiary` | Maneja ese `422`, corrige los datos y usa una clave nueva. Ver [Pago LatAm](/docs/recetas/pago-latam/) |
| El alta `latam` no recibía tipo de cuenta, y Perú no se podía registrar | Acepta `account_type`, y Perú lo exige | Envíalo cuando `account_type_required` sea `true` |
| Países y bancos se pedían a tu ejecutivo | [`GET /v1/latam/countries`](/docs/operaciones/listar-paises-latam/) y [`GET /v1/latam/countries/{country}/entities`](/docs/operaciones/listar-bancos-latam/) | Consúltalos en vez de fijarlos en tu código |

## Pagos y errores

| Antes | Ahora | Qué hacer |
|---|---|---|
| `failure_code` podía ser `null` en un pago `failed` | Nunca es `null` en un pago `failed`, tampoco si el detalle ya no se puede leer | Quita la rama de `failed` sin código. Sigue tolerando códigos nuevos |
| Los eventos `payment.*` no traían `failure_code` | Todos lo traen, con valor sólo en `payment.failed` | Léelo del evento si te sirve |
| Un rechazo registrado podía decir `retryable: true` | Siempre dice `retryable: false`, también con `in_review` | Decide por `retryable` de la respuesta, no por el código. Ver [Qué hacer con la clave tras un rechazo](/docs/idempotencia/#qué-hacer-con-la-clave-tras-un-rechazo) |
| El tope del cuerpo no estaba declarado | `413` a partir de 64 KB, con el código `invalid_request` | Parte los lotes grandes. Repetir no sirve. Ver [Límites](/docs/limites/#cuerpo) |
| Una `Idempotency-Key` con otro cuerpo dejaba de rechazarse a los 7 días | `422 idempotency_key_reused` también después | No reutilices claves |

## Cobros y altas con `202`

| Antes | Ahora | Qué hacer |
|---|---|---|
| [`POST /v1/collections`](/docs/operaciones/crear-cobro/) no comprobaba `payer`: en la cuenta de prueba, un cobro por SPEI sin `payer` daba `201` | Sin un `required`, o fuera de `options`, responde `400 invalid_request` con `payer.<campo>` en `details.fields`. La prueba por SPEI pide `firstName`, `lastName` y `email` | Envía `payer` según `payer_fields`. El cuerpo corregido necesita otra `Idempotency-Key` |
| Las claves de `payer` se pedían a tu ejecutivo | `payer_fields` de [`GET /v1/collection-methods`](/docs/operaciones/listar-metodos-de-cobro/) las da por método | Arma `payer` desde esa lista. Ver [Cobros](/docs/recetas/cobros/) |
| [`GET /v1/collections/{id}`](/docs/operaciones/consultar-cobro/) no daba `instruction.type` ni `expires_at` | Los da | Quita la copia que guardabas del alta |
| Repetir la clave tras un `202` de [`POST /v1/collections`](/docs/operaciones/crear-cobro/), [`POST /v1/deposit-addresses`](/docs/operaciones/crear-direccion-de-deposito/) o [`POST /v1/beneficiaries`](/docs/operaciones/crear-beneficiario/) repetía ese `202` | Entrega el resultado en cuanto existe | Repite con la misma clave hasta tener el resultado |

## Webhooks y eventos

| Antes | Ahora | Qué hacer |
|---|---|---|
| Los webhooks no traían `resource_id` | Lo traen, igual que [`GET /v1/events`](/docs/operaciones/listar-eventos/) | Úsalo para agrupar por recurso |
| `Event.type`, `Event.data` y `WebhookEndpoint.status` eran abiertos en el contrato | Son enumeraciones y formas declaradas | Regenera tu cliente. Sigue ignorando los tipos que no conozcas |
| `system.egress_ips.changing` no se emitía | Trae `ip`, `change`, `effective_at` y un `resource_id` con `egr_`. Llega una vez por destino, también al que se activa hasta una hora antes del cambio | Vigílalo si filtras por IP. Ver [IP de webhooks](/docs/seguridad/ip-webhooks/) |
| No había una respuesta sobre gestionar destinos por API | `v1` sólo los consulta, por decisión: todo cambio va por el panel | No esperes esas operaciones. Ver [Dar de alta un destino](/docs/webhooks/#dar-de-alta-un-destino) |

## Movimientos, recibos y llaves

| Antes | Ahora | Qué hacer |
|---|---|---|
| Una fila de [`GET /v1/movements`](/docs/operaciones/listar-movimientos/) que cambiaba podía perderse | Puede repetirse, y nunca se omite otra | Deduplica por `id` y guarda `next_cursor` en cada página. Ver [Paginación](/docs/paginacion/#movimientos-un-cursor-que-no-termina) |
| `completed_at` podía traer fecha en un recibo no completado | `null` si el movimiento no terminó en `completed` | No lo uses para fechar un pago devuelto |
| El calendario de avisos de caducidad no estaba publicado | Producción caduca a 180 días, y el panel avisa a 30, 7 y 1 días | Prevé la rotación. Ver [Vencimiento](/docs/autenticacion/#vencimiento) |

## Cuenta de prueba

| Antes | Ahora | Qué hacer |
|---|---|---|
| [`POST /sandbox/v1/webhooks/trigger`](/docs/operaciones/emitir-evento-de-prueba/) mandaba un `data` reducido | Manda la forma real de cada tipo, con datos ficticios y `simulated: true` | Prueba tu receptor con todos los tipos |
| No había catálogo `latam` | [`GET /sandbox/v1/latam/countries`](/docs/operaciones/listar-paises-latam/) da países ficticios con `available: false` | No esperes un alta `latam` aquí: responde `403 rail_not_enabled` |

## Errores comunes

- Tratar un `202` de alta como un fallo y cambiar de clave.
- Copiar la tabla de países en tu código en vez de consultar la API.
- Corregir `payer` y repetir con la misma clave.

## Qué sigue

[Cambios](/docs/cambios/) y [Versionado](/docs/versionado/).
