# API · Convenciones de las respuestas

> Cómo vienen tipados los montos, dónde hay JSON dentro de una cadena y por qué la misma entidad cambia de forma entre consultar uno y listar.

- kind: concept
- status: stable
- api_version: v1
- last_verified: 2026-09-02
- url: https://www.tilopay.com/developers/api/convenciones

Las respuestas del API de adquirencia tienen tres detalles que no se deducen leyendo cada
operación por separado y que rompen integraciones tipadas. Están acá porque aplican a
varias operaciones a la vez.

Para el manejo de errores, ver [cómo leer una respuesta de error](/developers/api/procesos-operativos/errors).

## Los montos vienen como cadena, no como número [#montos-cadena]

<Callout type="warn">
Los montos, descuentos e importes se serializan como cadena: `"discount": "40.00"`,
`"amount": "0.00"`, `"first_amount": "0.00"`, `"amount_max_daily": "1000.00"`.
</Callout>

Cualquier deserialización tipada que declare esos campos como número falla. Declaralos
como cadena y convertilos vos, con el tipo decimal de tu lenguaje, no con punto flotante.

Aparece, entre otras, en [obtener un cupón](/developers/api/recurrentes/cupones/get-coupon),
[listar cupones](/developers/api/recurrentes/cupones/get-coupons),
[listar planes](/developers/api/recurrentes/planes/get-plans) y
[pagos de suscriptor](/developers/api/recurrentes/suscriptores/suscriptor-payments).

## Hay un JSON dentro de una cadena [#doble-encoding]

En [pagos de suscriptor](/developers/api/recurrentes/suscriptores/suscriptor-payments), el
campo `data` de cada pago **no es un objeto: es una cadena que contiene JSON**.

```json
"data": "{\"first\":1,\"expire\":\"2024-12-28T19:05:29.572648Z\",\"amount\":0,\"activeAmount\":0,\"originalAmount\":0,\"key\":\"XXXX-XXXX-XXXX-XXXX-XXXX\"}"
```

Hay que deserializar dos veces: primero la respuesta, después el contenido de `data`.
Tratá esa segunda deserialización como puede fallar cualquier entrada externa: envolvela y
registrá la cadena cruda si no parsea.

## La misma entidad cambia de forma entre "obtener uno" y "listar" [#uno-vs-lista]

No es un descuido de la documentación: las dos operaciones devuelven formas distintas del
mismo cupón, y hay que mapearlas por separado.

| | [`getCoupon`](/developers/api/recurrentes/cupones/get-coupon) (uno) | [`getRepeatCoupons`](/developers/api/recurrentes/cupones/get-coupons) (lista) |
| --- | --- | --- |
| Fecha de creación | `created_at`, formato ISO | `create`, formato `Y-m-d H:i:s` |
| Otros campos | incluye `recurrent_id`, `updated_at` y `deleted_at` | no los incluye |

Lo mismo pasa en
[pagos de suscriptor](/developers/api/recurrentes/suscriptores/suscriptor-payments), que
también usa `create` con formato `Y-m-d H:i:s`.

## Dos formas de decir "no configurado" [#no-configurado]

En [listar planes](/developers/api/recurrentes/planes/get-plans), los campos de webhook de
un plan vienen como cadena vacía cuando no están configurados, mientras que `return_url`
viene como `null`. Son dos representaciones del mismo estado en el mismo objeto: al
verificar si un webhook está configurado, tratá la cadena vacía y `null` como el mismo
caso.
