Cómo montar suscripciones y cobro recurrente con la API Pagnovo
Productos, ofertas, suscripciones y facturas en la práctica — trial, ciclos, pausa y cancelación, estados de factura y los eventos que avisan antes del churn.
Equipo Pagnovo · 2026-08-07
El cobro recurrente parece simple hasta que hay que lidiar con trial, upgrade a mitad de ciclo, tarjeta vencida y cliente que pide pausa. Esta guía muestra cómo el modelo de la API Pagnovo organiza eso — y cómo usar sus eventos para reducir el churn.
Referencia completa: portal.pagnovo.com/docs.
El modelo: producto → oferta → suscripción → factura
La separación en cuatro capas parece burocracia al principio, pero es lo que evita retrabajo después:
| Capa | Qué representa | Ejemplo |
|---|---|---|
| Producto | Lo que vendes | "Plan Profesional" |
| Oferta | Cómo lo vendes | R$ 299/mes, 7 días de trial |
| Suscripción | Quién contrató qué | Cliente X contrató la oferta Y |
| Factura | Cada cobro generado | Cobro de marzo |
La ventaja: un mismo producto puede tener varias ofertas (mensual, anual, promocional, socio) sin duplicar registros. Y cambiar el precio de una oferta nueva no afecta a quien ya contrató la antigua.
Paso 1 — Cliente
POST /v2/customers
{
"name": "Maria Silva",
"email": "maria@empresa.com.br",
"document": "12345678909"
}
⚠️ El document (CPF/CNPJ) es único e inmutable. No se puede corregir después. Valídalo antes de
enviarlo — incluido el dígito verificador — porque un registro equivocado se convierte en un cliente
duplicado que arrastras para siempre.
También existe GET /v2/customers/:id/history, útil para atención: muestra el historial del cliente
sin que tengas que armarlo por tu lado.
Paso 2 — Producto y oferta
POST /v2/products
{ "name": "Plan Profesional" }
POST /v2/offers
{
"productId": "...",
"amount": 29900,
"billingCycle": "MONTHLY",
"billingCycleCount": 12,
"trialDays": 7,
"maxCycles": 24
}
Los campos que definen la economía del plan:
amount— en centavos (R$ 299,00 =29900)billingCycle—MONTHLYoYEARLYtrialDays— período gratuito antes del primer cobromaxCycles— finaliza la suscripción tras N ciclos (útil para planes con plazo definido)
Los cupones e intereses usan basis points, no porcentaje directo:
1000= 10%. Las multas fijas siguen en centavos. Confundirlo genera un descuento 100× mayor al pretendido.
Paso 3 — Suscripción
POST /v2/subscriptions
{
"customerId": "...",
"offerId": "...",
"amount": 29900,
"billingCycle": "MONTHLY"
}
Y el ciclo de vida completo, sin que tengas que implementarlo:
POST /v2/subscriptions/:id/pause → pausa temporal
POST /v2/subscriptions/:id/resume → reanuda
POST /v2/subscriptions/:id/cancel → cancela
PATCH /v2/subscriptions/:id → modifica (upgrade/downgrade)
Por qué "pausar" importa más de lo que parece
Cuando el cliente quiere irse, muchas veces no quiere irse para siempre — quiere parar dos meses. Si la única opción que ofreces es cancelar, conviertes una pausa en churn definitivo.
Ofrecer pausa en la pantalla de cancelación es una de las intervenciones de mayor retorno en productos de suscripción, y aquí es una sola llamada de API.
Paso 4 — Facturas y sus estados
Cada ciclo genera una factura, que recorre estos estados:
DRAFT → PENDING → PAID
↘ OVERDUE → (recuperación)
↘ CANCELED / EXPIRED / REFUNDED
| Estado | Significado |
|---|---|
DRAFT |
Creada, aún no emitida |
PENDING |
Emitida, esperando pago |
PAID |
Pagada |
OVERDUE |
Vencida — entra en la secuencia de cobranza |
REFUNDED |
Reembolsada |
CANCELED |
Cancelada antes del vencimiento |
EXPIRED |
Expiró sin pago |
Operaciones útiles: POST /v2/invoices/:id/cancel, POST /v2/invoices/:id/mark-refunded y
POST /v2/invoices/:id/notifications/resend — este último reenvía la notificación al cliente, el
primer paso de cualquier recuperación.
Paso 5 — Los eventos que evitan el churn
Aquí la integración deja de ser pasiva. Suscríbete a estos webhooks:
| Evento | Qué hacer |
|---|---|
subscription.created |
Registrar; aún no es ingreso confirmado |
subscription.activated |
Liberar acceso, iniciar onboarding |
subscription.past_due |
🚨 Actuar ahora — secuencia de cobranza |
subscription.paused |
Suspender acceso, agendar contacto de retorno |
subscription.canceled |
Cerrar acceso, lanzar encuesta de salida |
subscription.expired |
Cerrar ciclo, ofrecer renovación |
El evento más valioso es subscription.past_due. Avisa que el cobro falló — y la mayor parte de
esos fallos son involuntarios (tarjeta vencida, límite insuficiente), es decir, clientes que
quieren continuar.
Tratar el past_due con tono de aviso técnico, y no de cobranza, recupera mucho más:
✅ "¡Hola, Maria! El cobro de tu plan no pasó — normalmente es tarjeta vencida. Puedes actualizarla en 30 segundos aquí: [link]"
Combínalo con la secuencia de cobranza y reintentos espaciados.
Errores comunes
- Importe en reales.
299se vuelve R$ 2,99. Siempre centavos. - Confundir basis points.
10no es 10% — es 0,1%. - Tratar
createdcomo ingreso. Soloactivatedsignifica acceso liberado. - No ofrecer pausa. Convierte una pausa temporal en cancelación permanente.
- Ignorar
past_due. Es la alerta más accionable del conjunto. documentequivocado en el registro. Inmutable — se vuelve cliente duplicado.- Cancelación solo por teléfono. Además de molestar, genera contracargos: el cliente va al banco cuando no consigue cancelar contigo.
Checklist
-
documentvalidado antes de crear el cliente - Importes en centavos; cupones en basis points
- Ofertas separadas por modalidad (no editar una oferta activa)
- Acceso liberado en
subscription.activated, no encreated -
past_dueconectado a la secuencia de cobranza - Pausa ofrecida antes de la cancelación
- Cancelación autoservicio disponible
- Estados de factura reflejados en tu sistema
Conoce la plataforma de Cobros Recurrentes y la documentación de la API. Habla con el equipo para diseñar tu modelo de suscripción.