Documentación de la API
Guía completa para integrar Recaudo en tus aplicaciones
Autenticación
La API de Recaudo utiliza API keys para autenticar las peticiones. Crea una cuenta para obtener tus API keys.
Importante: Nunca expongas tu Secret Key en codigo del lado del cliente. Usa la Public Key para operaciones frontend.
Tipos de API Keys
| Tipo | Prefijo | Uso |
|---|---|---|
| Public Key (Live) | pk_live_ |
Frontend, checkout forms |
| Secret Key (Live) | sk_live_ |
Backend, operaciones sensibles |
| Public Key (Test) | pk_test_ |
Desarrollo frontend |
| Secret Key (Test) | sk_test_ |
Desarrollo backend |
Autenticacion via Header
Incluye tu API key en el header Authorization como Bearer token:
curl https://recaudo.app/api/v1/customers \
-H "Authorization: Bearer sk_live_tu_secret_key" \
-H "Content-Type: application/json"
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://recaudo.app/api/v1/customers', [
'headers' => [
'Authorization' => 'Bearer sk_live_tu_secret_key',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
]
]);
$data = json_decode($response->getBody(), true);
const response = await fetch('https://recaudo.app/api/v1/customers', {
method: 'GET',
headers: {
'Authorization': 'Bearer sk_live_tu_secret_key',
'Content-Type': 'application/json',
}
});
const data = await response.json();
import requests
response = requests.get(
'https://recaudo.app/api/v1/customers',
headers={
'Authorization': 'Bearer sk_live_tu_secret_key',
'Content-Type': 'application/json',
}
)
data = response.json()
Manejo de Errores
La API utiliza codigos de estado HTTP convencionales para indicar el exito o fallo de una peticion.
| Codigo | Descripcion |
|---|---|
| 200 | Peticion exitosa |
| 201 | Recurso creado exitosamente |
| 400 | Peticion invalida - revisa los parametros |
| 401 | No autorizado - API key invalida o faltante |
| 403 | Prohibido - no tienes permisos para este recurso |
| 404 | Recurso no encontrado |
| 422 | Error de validacion |
| 429 | Rate limit excedido |
| 500 | Error interno del servidor |
Formato de Error
{
"error": {
"type": "validation_error",
"code": "invalid_parameter",
"message": "El campo email es requerido",
"param": "email",
"doc_url": "https://docs.recaudo.com/errors/invalid_parameter"
}
}
Paginacion
Los endpoints que retornan listas de objetos soportan paginacion basada en cursor.
Parametros
| Parametro | Tipo | Descripcion |
|---|---|---|
limit |
integer | Numero de resultados por pagina (default: 10, max: 100) |
starting_after |
string | ID del ultimo objeto de la pagina anterior |
ending_before |
string | ID del primer objeto de la pagina siguiente (paginacion hacia atras) |
Ejemplo de Respuesta
{
"object": "list",
"data": [...],
"has_more": true,
"total_count": 150
}
Clientes
Los clientes representan a tus usuarios finales. Puedes asociarles metodos de pago, suscripciones y realizar cargos.
/v1/customers
Crear Cliente
Crea un nuevo cliente en tu cuenta.
Parametros
| Parametro | Tipo | Requerido | Descripcion |
|---|---|---|---|
email |
string | Sí | Email del cliente |
name |
string | Sí | Nombre completo |
phone |
string | No | Teléfono |
external_id |
string | No | ID del cliente en tu sistema |
tax_id |
string | No | Identificación fiscal (RFC) |
address |
string | No | Dirección (calle y número). Requerido para AMEX |
city |
string | No | Ciudad. Requerido para AMEX |
state |
string | No | Estado o provincia. Requerido para AMEX |
postal_code |
string | No | Código postal. Requerido para AMEX |
country |
string | No | Código de país ISO 3166-1 alpha-2 (ej: MX, US, CO). Requerido para AMEX |
metadata |
object | No | Datos adicionales (max 50 keys) |
curl -X POST https://recaudo.app/api/v1/customers \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"email": "cliente@ejemplo.com",
"name": "Juan Pérez",
"phone": "+52 55 1234 5678",
"address": "Av. Reforma 123, Col. Centro",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "06600",
"country": "MX",
"tax_id": "XAXX010101000",
"metadata": {
"user_id": "usr_123"
}
}'
$response = $client->post('/api/v1/customers', [
'json' => [
'email' => 'cliente@ejemplo.com',
'name' => 'Juan Pérez',
'phone' => '+52 55 1234 5678',
'address' => 'Av. Reforma 123, Col. Centro',
'city' => 'Ciudad de México',
'state' => 'CDMX',
'postal_code' => '06600',
'country' => 'MX',
'tax_id' => 'XAXX010101000',
'metadata' => ['user_id' => 'usr_123']
]
]);
const customer = await fetch('/api/v1/customers', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_xxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: 'cliente@ejemplo.com',
name: 'Juan Pérez',
phone: '+52 55 1234 5678',
address: 'Av. Reforma 123, Col. Centro',
city: 'Ciudad de México',
state: 'CDMX',
postal_code: '06600',
country: 'MX',
tax_id: 'XAXX010101000',
metadata: { user_id: 'usr_123' }
})
});
response = requests.post(
'https://recaudo.app/api/v1/customers',
headers={'Authorization': 'Bearer sk_live_xxx'},
json={
'email': 'cliente@ejemplo.com',
'name': 'Juan Pérez',
'phone': '+52 55 1234 5678',
'address': 'Av. Reforma 123, Col. Centro',
'city': 'Ciudad de México',
'state': 'CDMX',
'postal_code': '06600',
'country': 'MX',
'tax_id': 'XAXX010101000',
'metadata': {'user_id': 'usr_123'}
}
)
{
"object": "customer",
"data": {
"id": "cus_a1b2c3d4e5f6",
"email": "cliente@ejemplo.com",
"name": "Juan Pérez",
"phone": "+52 55 1234 5678",
"address": "Av. Reforma 123, Col. Centro",
"city": "Ciudad de México",
"state": "CDMX",
"postal_code": "06600",
"country": "MX",
"tax_id": "XAXX010101000",
"metadata": { "user_id": "usr_123" },
"created_at": "2024-01-15T10:30:00Z"
}
}
/v1/customers
Listar Clientes
Obtiene una lista paginada de todos los clientes.
curl https://recaudo.app/api/v1/customers?limit=10 \
-H "Authorization: Bearer sk_live_xxx"
{
"object": "list",
"data": [
{
"id": "cus_a1b2c3d4e5f6",
"object": "customer",
"email": "cliente@ejemplo.com",
"name": "Juan Perez",
...
}
],
"has_more": true,
"total_count": 150
}
/v1/customers/:id
Obtener Cliente
Obtiene los detalles de un cliente existente.
/v1/customers/:id
Actualizar Cliente
Actualiza los datos de un cliente. Solo se modifican los campos enviados.
/v1/customers/:id
Eliminar Cliente
Elimina un cliente y sus datos asociados. Esta accion no se puede deshacer.
Links de Pago
Los links de pago te permiten cobrar a tus clientes sin necesidad de integracion compleja. Genera un link y compartelo.
/v1/payment-links
Crear Link de Pago
Crea un nuevo link de pago.
Parametros
| Parametro | Tipo | Requerido | Descripcion |
|---|---|---|---|
amount |
integer | Si | Monto en centavos (ej: 10000 = $100.00) |
currency |
string | Si | Codigo ISO (MXN, USD, COP, etc.) |
description |
string | No | Descripcion del cobro |
customer_id |
string | No | ID del cliente a asociar |
expires_at |
timestamp | No | Fecha de expiracion (ISO 8601) |
success_url |
string | No | URL de redireccion tras pago exitoso |
curl -X POST https://recaudo.app/api/v1/payment-links \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 50000,
"currency": "MXN",
"description": "Pago de servicio mensual",
"customer_id": "cus_a1b2c3d4e5f6",
"success_url": "https://tuapp.com/pago-exitoso",
"metadata": {
"order_id": "ORD-123"
}
}'
$response = $client->post('/api/v1/payment-links', [
'json' => [
'amount' => 50000,
'currency' => 'MXN',
'description' => 'Pago de servicio mensual',
'customer_id' => 'cus_a1b2c3d4e5f6',
'success_url' => 'https://tuapp.com/pago-exitoso',
'metadata' => [
'order_id' => 'ORD-123'
]
]
]);
const paymentLink = await fetch('/api/v1/payment-links', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_xxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 50000,
currency: 'MXN',
description: 'Pago de servicio mensual',
customer_id: 'cus_a1b2c3d4e5f6',
success_url: 'https://tuapp.com/pago-exitoso',
metadata: { order_id: 'ORD-123' }
})
});
{
"id": "pl_x1y2z3a4b5c6",
"object": "payment_link",
"url": "https://pay.recaudo.com/pl_x1y2z3a4b5c6",
"amount": 50000,
"currency": "MXN",
"description": "Pago de servicio mensual",
"status": "pending",
"customer_id": "cus_a1b2c3d4e5f6",
"success_url": "https://tuapp.com/pago-exitoso",
"metadata": {
"order_id": "ORD-123"
},
"expires_at": null,
"created_at": "2024-01-15T10:30:00Z"
}
Otros Endpoints
/v1/payment-links
Listar links de pago
/v1/payment-links/:id
Obtener link
/v1/payment-links/:id
Actualizar link
/v1/payment-links/:id/cancel
Cancelar link
Cargos Directos
Realiza cargos directos a metodos de pago guardados. Ideal para cargos recurrentes o one-click payments.
/v1/charges
Crear Cargo
Realiza un cargo a un metodo de pago guardado.
Parametros
| Parametro | Tipo | Requerido | Descripcion |
|---|---|---|---|
amount |
integer | Si | Monto en centavos |
currency |
string | Si | Codigo ISO de moneda |
payment_method |
string | Condicional* | ID del método de pago guardado |
token |
string | Condicional* | Token de tarjeta generado con recaudo.js (tok_xxx para Conekta, pm_xxx para Stripe) |
card |
object | No | Info de tarjeta de recaudo.js: { brand, last4, exp_month, exp_year }. Recomendado para mejor tracking |
customer |
string | Condicional* | ID del cliente (requerido si usa payment_method) |
description |
string | No | Descripcion del cargo |
curl -X POST https://recaudo.app/api/v1/charges \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 25000,
"currency": "MXN",
"token": "tok_abc123...",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2027
},
"description": "Cargo por servicio premium"
}'
{
"id": "ch_abc123def456",
"object": "charge",
"amount": 25000,
"currency": "MXN",
"status": "succeeded",
"customer_id": "cus_a1b2c3d4e5f6",
"payment_method_id": "pm_card_xyz123",
"description": "Cargo por servicio premium",
"payment_intent_id": "pi_789xyz",
"receipt_url": "https://pay.recaudo.com/receipts/ch_abc123",
"created_at": "2024-01-15T10:30:00Z"
}
/v1/charges/:id/refund
Reembolsar Cargo
Realiza un reembolso total o parcial de un cargo.
curl -X POST https://recaudo.app/api/v1/charges/ch_abc123/refund \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"amount": 10000,
"reason": "customer_request"
}'
{
"id": "re_xyz789abc",
"object": "refund",
"amount": 10000,
"currency": "MXN",
"charge_id": "ch_abc123def456",
"status": "succeeded",
"reason": "customer_request",
"created_at": "2024-01-15T11:00:00Z"
}
Tokenización
Tokeniza tarjetas de forma segura directamente desde tu frontend usando recaudo.js v2. La librería carga automáticamente el SDK del procesador de pagos (Conekta, Stripe) configurado en tu cuenta y tokeniza la tarjeta de manera transparente.
Integración en 4 pasos
Incluir la librería JavaScript
<script src="https://recaudo.app/js/recaudo.min.js"></script>
Inicializar la librería
Inicializa recaudo.js con tu Publishable Key. El método init() carga el SDK del procesador configurado.
// Crear instancia con tu Publishable Key
const recaudo = new Recaudo('pk_live_tu_publishable_key');
// Inicializar (carga el SDK del procesador automáticamente)
await recaudo.init();
// Verificar que está listo
console.log('Gateway:', recaudo.getGateway()); // 'conekta' o 'stripe'
Tokenizar la tarjeta
Los datos de la tarjeta se tokenizan directamente con el procesador de pagos. Nunca pasan por tu servidor.
async function handlePayment() {
try {
const token = await recaudo.createToken({
number: '4242424242424242',
exp_month: 12,
exp_year: 2027,
cvv: '123',
name: 'Juan Pérez',
email: 'juan@example.com',
// Campos opcionales (requeridos para AMEX)
address: 'Av. Reforma 123',
city: 'CDMX',
state: 'CDMX',
postal_code: '06600',
country: 'MX'
});
// token contiene:
// - id: Token del procesador (tok_xxx para Conekta, pm_xxx para Stripe)
// - gateway: 'conekta' o 'stripe'
// - card: { brand, last4, exp_month, exp_year }
// Enviar al backend para procesar
await fetch('/tu-backend/procesar-pago', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: token.id,
card: token.card, // Incluir info de tarjeta para mejor tracking
amount: 10000
})
});
} catch (error) {
// error.type: 'validation_error', 'gateway_error', 'sdk_error'
console.error('Error:', error.message);
}
}
Procesar el cobro desde tu backend
Usa el token para crear un cargo o una suscripción. Incluye el objeto card para mejor tracking.
curl -X POST https://recaudo.app/api/v1/charges \
-H "Authorization: Bearer sk_live_tu_secret_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 10000,
"currency": "MXN",
"token": "tok_abc123...",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2027
},
"description": "Compra en tienda",
"receipt_email": "cliente@example.com"
}'
curl -X POST https://recaudo.app/api/v1/subscriptions \
-H "Authorization: Bearer sk_live_tu_secret_key" \
-H "Content-Type: application/json" \
-d '{
"plan": "plan_uuid_here",
"customer": "cus_uuid_here",
"token": "tok_abc123...",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2027
}
}'
Objeto Token (respuesta de createToken)
{
"id": "tok_2sR5Vx1J5K...", // Token del procesador (tok_xxx Conekta, pm_xxx Stripe)
"gateway": "conekta", // Procesador utilizado
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2027
},
"created_at": "2024-01-15T10:30:00.000Z"
}
Endpoints de la API
| Método | Endpoint | Descripción |
|---|---|---|
| GET | /v1/tokens/config |
Obtener configuración del procesador (usado por recaudo.js para cargar el SDK correcto) |
| GET | /v1/tokens/public-key |
Obtener clave pública para encriptación (legacy v1) |
| POST | /v1/tokens |
Crear token desde datos encriptados (legacy v1) |
| GET | /v1/tokens/:token |
Obtener información de un token |
| DELETE | /v1/tokens/:token |
Eliminar un token |
Métodos de recaudo.js
| Método | Descripción |
|---|---|
new Recaudo(publishableKey) |
Constructor. Crea una instancia con tu Publishable Key. |
init() |
Carga el SDK del procesador configurado. Debe llamarse antes de createToken(). |
createToken(cardData) |
Tokeniza los datos de la tarjeta con el procesador. |
getGateway() |
Retorna el nombre del procesador ('conekta', 'stripe'). |
isInitialized() |
Retorna true si init() ya fue llamado. |
detectCardBrand(number) |
Detecta la marca de la tarjeta (visa, mastercard, amex, etc). |
Manejo de Errores
Los errores de recaudo.js incluyen un tipo para facilitar su manejo:
try {
await recaudo.init();
const token = await recaudo.createToken(cardData);
} catch (error) {
switch (error.type) {
case 'validation_error':
// Datos de tarjeta inválidos
showError(error.message);
break;
case 'gateway_error':
// Error del procesador (tarjeta rechazada, etc)
showError(error.message);
break;
case 'sdk_error':
// Error cargando el SDK del procesador
showError('Error de conexión. Intenta de nuevo.');
break;
case 'configuration_error':
// Procesador no configurado
showError('Error de configuración.');
break;
default:
showError('Error inesperado.');
}
}
Suscripciones
Las suscripciones te permiten cobrar de forma recurrente a tus clientes. Soportan periodos de prueba, pausas y cambios de plan.
/v1/subscriptions
Crear Suscripción
Crea una nueva suscripción para un cliente. Puedes usar un método de pago almacenado o un token de recaudo.js.
Tokenización: Puedes usar el parámetro token con un token de recaudo.js (tok_xxx) en lugar de payment_method. El token será convertido automáticamente en un método de pago almacenado.
Parámetros
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
customer |
string (uuid) | Sí | ID del cliente |
plan |
string (uuid) | Sí | ID del plan de suscripción |
payment_method |
string (uuid) | No* | ID de método de pago almacenado. Requerido si no hay trial y no se usa token. |
token |
string | No* | Token de recaudo.js (tok_xxx para Conekta, pm_xxx para Stripe). Alternativa a payment_method. |
card |
object | No | Info de tarjeta de recaudo.js: { brand, last4, exp_month, exp_year }. Recomendado para mejor tracking |
trial_days |
integer | No | Días de prueba (0-365) |
amount |
numeric | No | Monto personalizado (override del plan) |
metadata |
object | No | Datos adicionales |
curl -X POST https://recaudo.app/api/v1/subscriptions \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"customer": "550e8400-e29b-41d4-a716-446655440000",
"plan": "660e8400-e29b-41d4-a716-446655440001",
"token": "tok_abc123def456",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2027
},
"metadata": {
"signup_source": "landing_page"
}
}'
curl -X POST https://recaudo.app/api/v1/subscriptions \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"customer": "550e8400-e29b-41d4-a716-446655440000",
"plan": "660e8400-e29b-41d4-a716-446655440001",
"payment_method": "770e8400-e29b-41d4-a716-446655440002",
"metadata": {
"signup_source": "landing_page"
}
}'
{
"id": "sub_1a2b3c4d5e6f",
"object": "subscription",
"status": "active",
"customer_id": "cus_a1b2c3d4e5f6",
"plan": {
"id": "plan_premium_monthly",
"name": "Premium Mensual",
"amount": 29900,
"currency": "MXN",
"interval": "month"
},
"current_period_start": "2024-01-15T00:00:00Z",
"current_period_end": "2024-02-15T00:00:00Z",
"trial_end": null,
"cancel_at_period_end": false,
"created_at": "2024-01-15T10:30:00Z"
}
Acciones de Suscripcion
/v1/subscriptions/:id/cancel
Cancelar suscripcion
curl -X POST https://recaudo.app/api/v1/subscriptions/sub_1a2b3c/cancel \
-H "Authorization: Bearer sk_live_xxx" \
-d '{"cancel_at_period_end": true, "reason": "too_expensive"}'
/v1/subscriptions/:id/pause
Pausar suscripcion
curl -X POST https://recaudo.app/api/v1/subscriptions/sub_1a2b3c/pause \
-H "Authorization: Bearer sk_live_xxx"
/v1/subscriptions/:id/resume
Reanudar suscripcion
curl -X POST https://recaudo.app/api/v1/subscriptions/sub_1a2b3c/resume \
-H "Authorization: Bearer sk_live_xxx"
Facturas
Las facturas permiten gestionar cobros con soporte completo para parcialidades (pagos en cuotas), políticas de moratorios y seguimiento detallado de pagos. Ideal para colegiaturas, créditos y servicios recurrentes.
Endpoints Disponibles
/v1/invoices
Listar facturas
/v1/invoices
Crear factura (con o sin parcialidades)
/v1/invoices/:id
Obtener factura
/v1/invoices/:id
Actualizar factura
/v1/invoices/:id
Eliminar factura
/v1/invoices/:id/pay
Marcar como pagada
/v1/invoices/:id/void
Anular factura
/v1/invoices/:id/installments
Listar parcialidades
/v1/invoices/:id/installments/:installment_id
Actualizar parcialidad
Crear Factura
Crea una factura con items y opcionalmente configura parcialidades automáticas o manuales.
curl -X POST https://recaudo.app/api/v1/invoices \
-H "Authorization: Bearer sk_live_tu_secret_key" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_abc123",
"document_date": "2024-01-15",
"items": [
{
"description": "Colegiatura Enero 2024",
"quantity": 1,
"unit_price": 5000.00
}
],
"has_installments": true,
"installment_count": 3,
"installment_frequency": "monthly",
"first_installment_due_date": "2024-01-20"
}'
$response = $client->post('https://recaudo.app/api/v1/invoices', [
'headers' => [
'Authorization' => 'Bearer sk_live_tu_secret_key',
'Content-Type' => 'application/json',
],
'json' => [
'customer_id' => 'cus_abc123',
'document_date' => '2024-01-15',
'items' => [
[
'description' => 'Colegiatura Enero 2024',
'quantity' => 1,
'unit_price' => 5000.00,
]
],
'has_installments' => true,
'installment_count' => 3,
'installment_frequency' => 'monthly',
'first_installment_due_date' => '2024-01-20',
]
]);
const response = await fetch('https://recaudo.app/api/v1/invoices', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_tu_secret_key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
customer_id: 'cus_abc123',
document_date: '2024-01-15',
items: [
{
description: 'Colegiatura Enero 2024',
quantity: 1,
unit_price: 5000.00
}
],
has_installments: true,
installment_count: 3,
installment_frequency: 'monthly',
first_installment_due_date: '2024-01-20'
})
});
response = requests.post(
'https://recaudo.app/api/v1/invoices',
headers={
'Authorization': 'Bearer sk_live_tu_secret_key',
'Content-Type': 'application/json',
},
json={
'customer_id': 'cus_abc123',
'document_date': '2024-01-15',
'items': [
{
'description': 'Colegiatura Enero 2024',
'quantity': 1,
'unit_price': 5000.00
}
],
'has_installments': True,
'installment_count': 3,
'installment_frequency': 'monthly',
'first_installment_due_date': '2024-01-20'
}
)
Parámetros de Parcialidades
| Parámetro | Tipo | Descripción |
|---|---|---|
has_installments |
boolean | Habilitar parcialidades |
installment_count |
integer | Número de parcialidades (2-48) |
installment_frequency |
string | weekly, biweekly o monthly |
first_installment_due_date |
date | Fecha de vencimiento de la primera parcialidad |
late_fee_policy_id |
uuid | ID de política de moratorios |
Parcialidades Manuales
Para mayor control, puedes definir cada parcialidad manualmente con sus propias fechas, montos y estado de pago. Útil para importar facturas existentes con pagos parciales.
{
"customer_id": "cus_abc123",
"items": [
{ "description": "Servicio anual", "quantity": 1, "unit_price": 12000.00 }
],
"installments": [
{
"due_date": "2024-01-15",
"amount": 4000.00,
"external_id": "FACT-001-P1",
"is_paid": true,
"amount_paid": 4000.00,
"paid_at": "2024-01-10"
},
{
"due_date": "2024-02-15",
"amount": 4000.00,
"external_id": "FACT-001-P2",
"is_paid": false
},
{
"due_date": "2024-03-15",
"amount": 4000.00,
"external_id": "FACT-001-P3",
"is_paid": false
}
]
}
Objeto Invoice
{
"id": "inv_abc123xyz",
"object": "invoice",
"invoice_number": "INV-2024-0001",
"external_id": "FACT-001",
"status": "partial",
"customer_id": "cus_a1b2c3d4e5f6",
"currency": "MXN",
"subtotal": 5000.00,
"tax_amount": 0,
"total": 5000.00,
"amount_paid": 1666.67,
"amount_due": 3333.33,
"document_date": "2024-01-15",
"due_date": "2024-03-20",
"has_installments": true,
"installment_count": 3,
"installment_frequency": "monthly",
"items": [
{
"id": "ii_xyz789",
"description": "Colegiatura Enero 2024",
"quantity": 1,
"unit_price": 5000.00,
"total": 5000.00
}
],
"installments": [
{
"id": "inst_001",
"installment_number": 1,
"status": "paid",
"principal_amount": 1666.67,
"late_fee_amount": 0,
"total_amount": 1666.67,
"amount_paid": 1666.67,
"amount_due": 0,
"due_date": "2024-01-20",
"paid_at": "2024-01-18T14:30:00Z",
"days_overdue": 0,
"external_id": null
},
{
"id": "inst_002",
"installment_number": 2,
"status": "pending",
"principal_amount": 1666.67,
"late_fee_amount": 0,
"total_amount": 1666.67,
"amount_paid": 0,
"amount_due": 1666.67,
"due_date": "2024-02-20",
"paid_at": null,
"days_overdue": 0,
"external_id": null
},
{
"id": "inst_003",
"installment_number": 3,
"status": "pending",
"principal_amount": 1666.66,
"late_fee_amount": 0,
"total_amount": 1666.66,
"amount_paid": 0,
"amount_due": 1666.66,
"due_date": "2024-03-20",
"paid_at": null,
"days_overdue": 0,
"external_id": null
}
],
"created_at": "2024-01-15T00:00:00Z"
}
Estados de Parcialidades
| Estado | Descripción |
|---|---|
| pending | Pendiente de pago, no vencida |
| partial | Pago parcial recibido |
| paid | Completamente pagada |
| overdue | Vencida sin pagar (puede generar moratorios) |
| cancelled | Cancelada (factura anulada) |
Moratorios
Las parcialidades vencidas pueden generar moratorios automáticamente según la política configurada.
El campo late_fee_amount muestra el moratorio acumulado
y se suma al total_amount de la parcialidad.
Importante: Los moratorios se calculan sobre el saldo pendiente del principal, no sobre moratorios previos (evita interés compuesto no deseado).
Webhooks
Los webhooks te permiten recibir notificaciones en tiempo real sobre eventos en tu cuenta. Configura un endpoint HTTPS y recibe actualizaciones automaticas.
Tipos de Eventos
Pagos
payment.createdpayment.succeededpayment.failedpayment.refunded
Links de Pago
payment_link.createdpayment_link.paidpayment_link.expired
Suscripciones
subscription.createdsubscription.updatedsubscription.canceledsubscription.pausedsubscription.resumedsubscription.trial_ending
Facturas
invoice.createdinvoice.paidinvoice.payment_failedinvoice.upcoming
Formato del Payload
{
"id": "evt_1a2b3c4d5e6f",
"object": "event",
"type": "payment.succeeded",
"api_version": "2024-01-01",
"created": 1705312200,
"data": {
"object": {
"id": "pi_abc123xyz",
"object": "payment_intent",
"amount": 50000,
"currency": "MXN",
"status": "succeeded",
"customer_id": "cus_a1b2c3d4e5f6",
...
}
}
}
Verificacion de Firma (HMAC)
Cada webhook incluye un header X-Recaudo-Signature que debes verificar
para asegurar que el mensaje viene de Recaudo.
<?php
// Obtener el payload y la firma
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_RECAUDO_SIGNATURE'];
$secret = 'whsec_tu_webhook_secret';
// Extraer timestamp y firma del header
// Formato: t=timestamp,v1=signature
$parts = explode(',', $signature);
$timestamp = substr($parts[0], 2);
$receivedSignature = substr($parts[1], 3);
// Calcular la firma esperada
$signedPayload = $timestamp . '.' . $payload;
$expectedSignature = hash_hmac('sha256', $signedPayload, $secret);
// Verificar
if (hash_equals($expectedSignature, $receivedSignature)) {
// Firma valida - procesar el evento
$event = json_decode($payload, true);
switch ($event['type']) {
case 'payment.succeeded':
// Manejar pago exitoso
break;
case 'subscription.canceled':
// Manejar cancelacion
break;
}
http_response_code(200);
} else {
// Firma invalida
http_response_code(400);
}
const crypto = require('crypto');
app.post('/webhooks/recaudo', express.raw({type: 'application/json'}), (req, res) => {
const payload = req.body;
const signature = req.headers['x-recaudo-signature'];
const secret = 'whsec_tu_webhook_secret';
// Extraer timestamp y firma
const parts = signature.split(',');
const timestamp = parts[0].split('=')[1];
const receivedSignature = parts[1].split('=')[1];
// Calcular firma esperada
const signedPayload = `${timestamp}.${payload}`;
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
// Verificar
if (crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(receivedSignature)
)) {
const event = JSON.parse(payload);
switch (event.type) {
case 'payment.succeeded':
// Manejar pago exitoso
break;
case 'subscription.canceled':
// Manejar cancelacion
break;
}
res.status(200).send('OK');
} else {
res.status(400).send('Invalid signature');
}
});
import hmac
import hashlib
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhooks/recaudo', methods=['POST'])
def webhook():
payload = request.data
signature = request.headers.get('X-Recaudo-Signature')
secret = 'whsec_tu_webhook_secret'
# Extraer timestamp y firma
parts = signature.split(',')
timestamp = parts[0].split('=')[1]
received_signature = parts[1].split('=')[1]
# Calcular firma esperada
signed_payload = f"{timestamp}.{payload.decode()}"
expected_signature = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
# Verificar
if hmac.compare_digest(expected_signature, received_signature):
event = request.json
if event['type'] == 'payment.succeeded':
# Manejar pago exitoso
pass
elif event['type'] == 'subscription.canceled':
# Manejar cancelacion
pass
return 'OK', 200
else:
return 'Invalid signature', 400
Mejores Practicas
- Siempre verifica la firma HMAC antes de procesar el evento
- Responde con 200 rapidamente y procesa de forma asincrona
- Implementa idempotencia usando el ID del evento
- Usa HTTPS con un certificado valido
- Los webhooks se reintentan automaticamente si fallas (hasta 3 veces)
Flujos de Conversación
La API de Flujos de Conversación te permite desplegar flujos automatizados de WhatsApp a tus clientes, similar a como funcionan las plataformas de engagement como Treble. Puedes iniciar campañas masivas, monitorear el estado de las ejecuciones y actualizar variables en tiempo real.
/api/v1/flows
Lista todos los flujos de conversación disponibles para tu tenant.
Respuesta
{
"object": "list",
"data": [
{
"id": "01234567-89ab-cdef-0123-456789abcdef",
"name": "Flujo de Cobranza",
"description": "Recordatorio de pago con opciones",
"type": "collection",
"purpose": "Recordatorio",
"channel": "whatsapp",
"status": "published",
"version": 1,
"total_runs": 150,
"total_responses": 89,
"response_rate": 59.3,
"published_at": "2024-01-15T10:30:00Z",
"created_at": "2024-01-10T08:00:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
],
"url": "/v1/flows"
}
/api/v1/flows/{flow_id}/deploy
Despliega un flujo a uno o más destinatarios. Cada destinatario recibirá el flujo de forma independiente. Puedes incluir variables personalizadas que se usarán para reemplazar placeholders en los mensajes.
Cuerpo de la Petición
{
"recipients": [
{
"phone": "+5215512345678",
"variables": {
"nombre": "Juan Pérez",
"monto": "$1,500.00",
"fecha_limite": "15 de febrero"
}
},
{
"phone": "+5215587654321",
"variables": {
"nombre": "María García",
"monto": "$2,300.00",
"fecha_limite": "20 de febrero"
}
}
],
"scheduled_at": null // Opcional: programar para después
}
Respuesta
{
"success": true,
"message": "Flow deployed to 2 recipients",
"data": {
"flow_id": "01234567-89ab-cdef-0123-456789abcdef",
"batch_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"total_recipients": 2,
"runs_created": 2,
"scheduled_at": null,
"run_ids": [
"f1a2b3c4-d5e6-7890-abcd-111111111111",
"f1a2b3c4-d5e6-7890-abcd-222222222222"
]
}
}
/api/v1/flows/{flow_id}/runs
Lista las ejecuciones de un flujo con filtros opcionales.
Parámetros de Query
| Parámetro | Tipo | Descripción |
|---|---|---|
| status | string | Filtrar por estado: pending, running, waiting, completed, failed, cancelled |
| batch_id | string | Filtrar por ID de lote (retornado en el deploy) |
| phone | string | Filtrar por número de teléfono del destinatario |
| limit | integer | Resultados por página (default 20) |
Respuesta
{
"object": "list",
"data": [
{
"id": "f1a2b3c4-d5e6-7890-abcd-111111111111",
"flow_id": "01234567-89ab-cdef-0123-456789abcdef",
"flow_name": "Flujo de Cobranza",
"phone_number": "+5215512345678",
"country_code": "MX",
"customer_id": "cust-uuid-123",
"customer_name": "Juan Pérez",
"conversation_id": "conv-uuid-789",
"status": "completed",
"source": "api",
"batch_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"context": {
"nombre": "Juan Pérez",
"monto": "$1,500.00"
},
"current_node_id": null,
"error_message": null,
"scheduled_at": null,
"started_at": "2024-01-20T10:00:00Z",
"finished_at": "2024-01-20T10:05:30Z",
"created_at": "2024-01-20T09:59:58Z"
}
],
"has_more": false,
"url": "/v1/flows/01234567-89ab-cdef-0123-456789abcdef/runs"
}
/api/v1/flows/{flow_id}/runs/{run_id}
Obtiene el detalle de una ejecución específica del flujo.
Respuesta
{
"object": "flow_run",
"id": "f1a2b3c4-d5e6-7890-abcd-111111111111",
"flow_id": "01234567-89ab-cdef-0123-456789abcdef",
"flow_name": "Flujo de Cobranza",
"phone_number": "+5215512345678",
"country_code": "MX",
"customer_id": "cust-uuid-123",
"customer_name": "Juan Pérez",
"conversation_id": "conv-uuid-789",
"status": "waiting",
"source": "api",
"batch_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"context": {
"nombre": "Juan Pérez",
"monto": "$1,500.00",
"respuesta_cliente": "Sí, pagaré mañana"
},
"current_node_id": "node_1768811907821_4wexo4g3h",
"error_message": null,
"scheduled_at": null,
"started_at": "2024-01-20T10:00:00Z",
"finished_at": null,
"created_at": "2024-01-20T09:59:58Z"
}
/api/v1/flows/{flow_id}/runs/{run_id}/variables
Actualiza las variables de una ejecución en curso. Útil para inyectar información externa que será usada en los siguientes nodos del flujo.
Cuerpo de la Petición
{
"variables": {
"crm_status": "contacted",
"agent_notes": "Cliente confirmó pago para mañana",
"follow_up_date": "2024-01-21"
}
}
/api/v1/flows/{flow_id}/runs/{run_id}/cancel
Cancela una ejecución que está pendiente o en espera. Las ejecuciones completadas o fallidas no pueden cancelarse.
Respuesta
{
"success": true,
"message": "Flow run cancelled successfully",
"data": {
"id": "f1a2b3c4-d5e6-7890-abcd-111111111111",
"flow_id": "01234567-89ab-cdef-0123-456789abcdef",
"flow_name": "Flujo de Cobranza",
"phone_number": "+5215512345678",
"country_code": "MX",
"customer_id": "cust-uuid-123",
"customer_name": "Juan Pérez",
"conversation_id": null,
"status": "cancelled",
"source": "api",
"batch_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"context": {
"nombre": "Juan Pérez"
},
"current_node_id": null,
"error_message": null,
"scheduled_at": null,
"started_at": null,
"finished_at": "2024-01-20T11:00:00Z",
"created_at": "2024-01-20T09:59:58Z"
}
}
Webhooks de Conversación
Configura webhooks para recibir notificaciones en tiempo real sobre eventos de conversación y flujos. Los webhooks se configuran desde el panel de Webhook Endpoints.
Eventos Disponibles
| Evento | Descripción |
|---|---|
| conversation.created | Nueva conversación iniciada |
| conversation.assigned | Conversación asignada a un agente |
| conversation.transferred | Conversación transferida a otro agente o grupo |
| conversation.resolved | Conversación marcada como resuelta |
| conversation.closed | Conversación cerrada con motivo |
| conversation.message.received | Mensaje recibido del cliente |
| conversation.message.sent | Mensaje enviado al cliente |
| flow_run.started | Ejecución de flujo iniciada |
| flow_run.completed | Ejecución de flujo completada |
| flow_run.failed | Ejecución de flujo fallida |
| flow_run.waiting | Flujo esperando respuesta del cliente |
Ejemplo de Payload (conversation.message.received)
{
"id": "evt_123456",
"type": "conversation.message.received",
"created_at": "2024-01-20T10:05:30Z",
"data": {
"id": "msg_abc123",
"conversation_id": "conv_xyz789",
"direction": "inbound",
"type": "text",
"body": "Sí, haré el pago mañana",
"sender_identifier": "+5215512345678",
"created_at": "2024-01-20T10:05:30Z",
"conversation": {
"id": "conv_xyz789",
"channel": "whatsapp",
"channel_identifier": "+5215512345678",
"status": "open",
"customer_id": "cust_abc",
"customer_name": "Juan Pérez",
"assigned_user_id": "user_123",
"assigned_user_name": "Ana López"
}
}
}
Casos de Uso
Campaña de Cobranza Masiva
Despliega un flujo de recordatorio de pago a miles de clientes con variables personalizadas (nombre, monto, fecha). El flujo puede incluir múltiples mensajes, esperas por respuesta, y ramificaciones basadas en las respuestas del cliente.
Integración con CRM
Usa los webhooks para sincronizar el estado de las conversaciones con tu CRM. Cuando un cliente responde, actualiza automáticamente su registro con la respuesta y programa seguimientos.
Encuestas y NPS
Despliega flujos interactivos con botones para recopilar feedback de clientes. Usa las variables del run para almacenar las respuestas y consultar los resultados vía API.