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

# Novedades

> Cambios del contrato de la TapiPay API: primero los que pueden afectar tu integración y después las correcciones.

<Update label="AAAA-MM-DD" description="Revisión del contrato de la API">
  Esta versión hace más estricta la validación de entrada y corrige varias respuestas para que coincidan con lo documentado. Revisa primero la lista de cambios que pueden afectar tu integración.

  ## Cambios que pueden afectar tu integración

  Son cambios en los que una petición que antes pasaba (o daba `5xx`) ahora devuelve `4xx`, o en los que cambia un valor de la respuesta que tu código podría estar leyendo.

  * **Booleanos estrictos** en `POST /v2/debts` y `POST`/`PATCH /subscriptions`: `allowOverduePayment`, `allowPartialPayments`, `autopay` y `enforcePaymentOrder` aceptan solo `true`/`false`. `"true"`, `1`, etc. devuelven `400` con `details[]`. `description` debe ser string (`400`). Ver [Convenciones de la API](/es/conceptos/convenciones#tipos-estrictos).
  * **`additionalData`** en `POST /v2/debts`, `POST /payment-links` y `POST`/`PATCH /subscriptions`: debe ser un objeto plano y no puede usar claves reservadas (`description`, `paymentMethods`, `productName`, etc.). Si no, `400`. Ver [Convenciones de la API](/es/conceptos/convenciones#additionaldata).
  * **`POST /payment-links`**: un `externalClientId` que no corresponde a un contacto devuelve `422 BUSINESS_RULE_VIOLATION` (antes se creaba la liga igual). Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **`POST`/`PATCH /payment-links`**: `expiresAt` debe ser un date-time RFC 3339 (`2026-12-31 23:59` o solo fecha devuelven `400`). En el `PATCH`, `expiresAt: null` devuelve `400` (antes `500`; las ligas siempre vencen), `metadata` `null` o que no sea objeto devuelve `400` y `description` vacía devuelve `400`. Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **`PATCH /subscriptions/{id}`**: valida tipos, el enum de `paymentMethods` y la exclusión entre `autopay: true` y `paymentMethods` (`400`). Una suscripción `CANCELLED` o `ENDED` devuelve `422` (antes `200`). Ver [Suscripciones](/es/recursos/suscripciones).
  * **`POST /subscriptions`**: `intervalUnit` distingue mayúsculas (`MONTH` devuelve `400`) y, si la programación es inválida, no se crea el producto ni el contacto en línea. Ver [Suscripciones](/es/recursos/suscripciones).
  * **`PATCH /contacts/{id}`**: `phones: []` devuelve `400` (antes `200` sin efecto). Ver [Contactos](/es/recursos/contactos).
  * **`GET /contacts`**: `identifier` y `externalClientId` con valores distintos devuelven `400`. Ver [Contactos](/es/recursos/contactos).
  * **`GET /products`**: `active` distinto de `true`/`false` devuelve `400` (antes se leía como `false`). Ver [Productos](/es/recursos/productos).
  * **`GET /payment-links`**: `createdAtFrom`/`createdAtTo` con formato inválido devuelven `400` (antes se comparaban como texto). Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **`GET /subscriptions`**: `externalClientId` ahora filtra (antes se ignoraba y devolvía suscripciones de otros contactos). Vacío o en blanco devuelve `400`. Ver [Suscripciones](/es/recursos/suscripciones).
  * **`GET /debts/{id}`, `PATCH /debts/{id}`, `POST /debts/{id}/cancel`**: una deuda cancelada se puede leer (`200`, `status: CANCELLED`); cancelarla otra vez o hacerle `PATCH` devuelve `422` (antes `404`). Ver [Deudas](/es/recursos/deudas).
  * **`POST /v2/debts`**: nuevo valor `autopayReason: "NO_PRODUCT"`. Si validas `autopayReason` contra una lista cerrada, agrégalo. Ver [Débito automático](/es/recursos/debito-automatico).
  * **Fechas en contactos, deudas y pagos de ligas**: los `date-time` se devuelven en UTC con milisegundos y `Z` (`2026-09-24T21:09:41.835Z`). Antes podían venir sin zona, con `+00:00` o con microsegundos. Ver [Convenciones de la API](/es/conceptos/convenciones#fechas).
  * **`PATCH /debts/{id}`**: `dueDate` en la respuesta pasa a `YYYY-MM-DD` (antes date-time). Ver [Deudas](/es/recursos/deudas).
  * **`POST /payment-links` sin deudor**: `paymentUrl` pasa a ser la URL corta (ya no contiene el `identifierValue`) y puede ser `null` si falla su generación. Trátala como una URL opaca. Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **Ligas de pago, todas las respuestas**: `expiresAt` se devuelve siempre en UTC con milisegundos y `Z` (`2026-12-31T23:59:59.000Z`), aunque se haya enviado con offset (`-06:00`). Las ligas antiguas guardadas con fecha sola (`2026-12-01`) salen como el inicio de ese día UTC (`2026-12-01T00:00:00.000Z`). Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **Ligas de pago sin llave externa, todas las respuestas**: `externalPaymentLinkId` es `null` cuando la liga se creó sin `externalPaymentLinkId` ni header `Idempotency-Key` (antes devolvía un valor `plk_<uuid>` igual al `paymentLinkId`). Ver [Ligas de pago](/es/recursos/ligas-de-pago#idempotencia).
  * **`GET /debts` y `GET /payment-links/{id}/payments`**: `createdAtTo` como fecha sola ahora incluye ese día (antes filtraba hasta su inicio). Si enviabas el día siguiente para compensar, ahora recibes un día de más. `createdAtFrom` posterior a `createdAtTo` devuelve `400` con el mensaje `createdAtFrom must be earlier than or equal to createdAtTo.` Ver [Deudas](/es/recursos/deudas).

  ## Correcciones

  * **`PATCH /debts/{id}` y `PATCH /subscriptions/{id}`**: el `PATCH` ya no reinicia `allowPartialPayments` a `false`, y `amountPaid` sale correcto (no `null`). Ver [Deudas](/es/recursos/deudas).
  * **`GET /debts/{id}` y `GET /debts`**: `description` aparece en las lecturas. Ver [Deudas](/es/recursos/deudas).
  * **`POST /v2/debts` y `POST /subscriptions`**: `contact.contactId` viene con el prefijo `con_` también cuando el contacto se crea en línea. Ver [Débito automático](/es/recursos/debito-automatico).
  * **`GET /contacts`**: `identifier` filtra, y `contactCode` acepta el prefijo `con_`. Ver [Contactos](/es/recursos/contactos).
  * **Contactos**: los contactos sin email creados en línea devuelven `email: null` (antes el texto `"null"`), también al leer los ya existentes. Ver [Contactos](/es/recursos/contactos).
  * **`GET /payment-links?externalPaymentLinkId=`**: deja de devolver `500`. Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **`POST /payment-links`**: un `externalPaymentLinkId` repetido devuelve `409` sin crear la deuda. Una liga con deudor ya no falla con `500` si la configuración del portal no responde. Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **`PATCH /payment-links/{id}`**: `expiresAt` actualiza también el vencimiento de la deuda de la liga. Si esa deuda ya fue cancelada, se actualiza solo la liga (antes el `PATCH` fallaba). Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **Respuesta de ligas de pago**: siempre trae `externalPaymentLinkId`, `expiresAt`, `successUrl`, `metadata`, `identifierValue`, `contact` y `product`, con `null` cuando no hay valor. Ver [Ligas de pago](/es/recursos/ligas-de-pago).
  * **`POST /products`**: devuelve lo que se guardó (timestamps, `externalProductId`, `active`) en lugar de repetir el request. Ver [Productos](/es/recursos/productos).
  * **`PATCH /products/{id}`**: `active: false` desactiva el producto; renombrar a un nombre ya usado, también por un producto inactivo, devuelve `409` (antes `500`); tipos inválidos devuelven `400` (antes `500`). Ver [Productos](/es/recursos/productos).
  * **`POST /subscriptions`**: `paymentUrl` ya no es `null`, y `scheduling.billingDay` se infiere de `startDate` (para `month` y `year`). Ver [Suscripciones](/es/recursos/suscripciones).
  * **`GET /debts` y `GET /payment-links/{id}/payments`**: `createdAtFrom`/`createdAtTo` aceptan date-time RFC 3339 además de fecha. Con la misma fecha en los dos devuelven las deudas de ese día (antes `400`), y un `createdAtFrom` futuro sin `createdAtTo` devuelve una página vacía (antes `400`). Ver [Deudas](/es/recursos/deudas).
  * **`PATCH` de contactos, productos, ligas de pago, suscripciones y deudas**: `companyCode` en el cuerpo se toma como contexto y ya no se rechaza como campo no editable. Ver [Convenciones de la API](/es/conceptos/convenciones#companycode).
  * **`POST /v2/debts` (lote)**: cada error de `details[]` trae `field`. Ver [Débito automático](/es/recursos/debito-automatico).
  * **`PATCH /debts/{id}`**: las fechas imposibles (`2026-02-30`) devuelven `400`. Ver [Deudas](/es/recursos/deudas).
  * **`POST /v2/debts`, `POST /payment-links` y suscripciones**: `additionalData` enviado como objeto se guarda en la deuda (antes se perdía en deudas y ligas). Ver [Convenciones de la API](/es/conceptos/convenciones#additionaldata).
  * **`PATCH /subscriptions/{id}`**: si falla la actualización de alguna deuda de la suscripción, la respuesta es `207` con `error.code: PARTIAL_UPDATE` (antes `500 INTERNAL_ERROR`). La suscripción queda actualizada. Ver [Suscripciones](/es/recursos/suscripciones#actualizar-una-suscripción).
  * **`paymentUrl` en deudas, suscripciones y ligas de pago**: si tu compañía no tiene configurados los links privados, `paymentUrl` ya no viene en `null`: se devuelve la URL del portal de tu compañía. Trátala igual como una URL opaca. Ver [Deudas](/es/recursos/deudas).
</Update>
