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.app/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 |
number | Si | Monto en decimales (ej: 100.00 = $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": 500.00,
"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' => 500.00,
'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: 500.00,
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.app/pl_x1y2z3a4b5c6",
"amount": 500.00,
"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 |
number | Si | Monto en decimales (ej: 100.00 = $100.00) |
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": 250.00,
"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": 250.00,
"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.app/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": 100.00,
"reason": "customer_request"
}'
{
"id": "re_xyz789abc",
"object": "refund",
"amount": 100.00,
"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: 100.00
})
});
} 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": 100.00,
"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": 299.00,
"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": 500.00,
"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)
Productos
Gestiona tu catálogo de productos y categorías. Los productos pueden tener control de inventario, precios de comparación, impuestos y metadatos personalizados.
Listar categorías
/v1/product-categories
Parámetros opcionales: is_active, parent_id (UUID o "root"), limit, page
Crear categoría
/v1/product-categories
{
"name": "Electrónicos",
"description": "Productos electrónicos y gadgets",
"parent_id": null,
"sort_order": 1,
"is_active": true
}
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Sí | Nombre de la categoría |
parent_id | uuid | No | Categoría padre (para subcategorías) |
description | string | No | Descripción |
sort_order | integer | No | Orden de visualización |
is_active | boolean | No | Si está activa (default: true) |
Obtener / Actualizar / Eliminar categoría
/v1/product-categories/{'{id}'}
/v1/product-categories/{'{id}'}
/v1/product-categories/{'{id}'}
Al eliminar una categoría, sus subcategorías se mueven al padre y los productos se desvinculan.
Listar productos
/v1/products
Parámetros opcionales: status (active, draft, archived), category (UUID), search, featured, in_stock, limit, page
Crear producto
/v1/products
{
"name": "Laptop Pro 15",
"price": 25999.00,
"currency": "MXN",
"sku": "LAP-PRO-15",
"description": "Laptop profesional de 15 pulgadas",
"product_category_id": "uuid-de-la-categoria",
"track_stock": true,
"stock_quantity": 50,
"tax_rate": 16,
"is_featured": true,
"metadata": {
"brand": "TechCorp",
"warranty_months": 24
}
}
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Sí | Nombre del producto |
price | number | Sí | Precio en decimales (100.00 = $100) |
currency | string | No | Moneda ISO 4217 (default según tenant) |
status | string | No | active, draft, archived (default: active) |
product_category_id | uuid | No | UUID de la categoría |
sku | string | No | Código SKU |
description | string | No | Descripción larga |
compare_at_price | number | No | Precio de comparación (antes) |
cost_price | number | No | Costo del producto |
track_stock | boolean | No | Activar control de inventario |
stock_quantity | integer | No | Cantidad en stock |
tax_rate | number | No | Tasa de impuesto (%) |
is_featured | boolean | No | Producto destacado |
image_url | url | No | URL de la imagen |
metadata | object | No | Metadatos personalizados |
Obtener / Actualizar / Eliminar producto
/v1/products/{'{id}'}
/v1/products/{'{id}'}
/v1/products/{'{id}'}
No se puede eliminar un producto con pedidos pendientes.
Pedidos
Crea y gestiona pedidos para tus clientes. Al crear un pedido se genera automáticamente una factura y un link de pago. Los precios se toman directamente del catálogo de productos.
Listar pedidos
/v1/orders
Parámetros opcionales: status, payment_status, customer (UUID), search, limit, page
Status: pending, confirmed, processing, shipped, delivered, completed, canceled, refunded
Payment status: pending, paid, partially_paid, refunded
Crear pedido
/v1/orders
{
"customer_id": "uuid-del-cliente",
"items": [
{ "product_id": "uuid-producto-1", "quantity": 2 },
{ "product_id": "uuid-producto-2", "quantity": 1 }
],
"notes": "Entregar antes de las 5pm",
"shipping_name": "Juan Pérez",
"shipping_phone": "+5215512345678",
"shipping_address": "Av. Reforma 123, CDMX"
}
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
customer_id | uuid | Sí | UUID del cliente |
items | array | Sí | Lista de productos con product_id y quantity |
notes | string | No | Notas visibles para el cliente |
internal_notes | string | No | Notas internas |
currency | string | No | Moneda ISO 4217 |
shipping_name | string | No | Nombre del destinatario |
shipping_phone | string | No | Teléfono del destinatario |
shipping_address | string | No | Dirección de envío |
metadata | object | No | Metadatos personalizados |
Obtener pedido
/v1/orders/{'{id}'}
Incluye cliente, items con productos y factura asociada.
Ciclo de vida del pedido
/v1/orders/{'{id}'}/cancel
— Cancelar pedido (restaura stock)
/v1/orders/{'{id}'}/ship
— Marcar como enviado
/v1/orders/{'{id}'}/deliver
— Marcar como entregado
/v1/orders/{'{id}'}/complete
— Marcar como completado
Flujo típico: pending → confirmed → shipped → delivered → completed
Items del pedido
/v1/orders/{'{id}'}/items
Retorna los items del pedido con detalle de cada producto.
Conversaciones
Envía y recibe mensajes a través de WhatsApp, Facebook Messenger e Instagram. La API detecta automáticamente el canal de cada conversación y enruta los mensajes al servicio correcto.
Listar conversaciones
/v1/conversations
Parámetros opcionales: channel (whatsapp, messenger, instagram), status (open, waiting, resolved, closed), customer (UUID), assigned_to (UUID), limit, page
Obtener conversación
/v1/conversations/{'{id}'}
Incluye cliente y agente asignado.
Listar mensajes
/v1/conversations/{'{id}'}/messages
Parámetros opcionales: type (text, image, video, audio, document, template), limit, page
Enviar mensaje
/v1/conversations/{'{id}'}/messages
{
"body": "Hola, tu pedido ha sido enviado. Número de guía: ABC123"
}
{
"body": "Aquí está tu factura",
"media_url": "https://ejemplo.com/factura.pdf",
"media_type": "document"
}
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
body | string | No* | Texto del mensaje (máx 4096 chars) |
media_url | url | No* | URL del archivo multimedia |
media_type | string | No | image, video, audio, document |
* Se requiere al menos body o media_url.
Enviar template de WhatsApp
/v1/conversations/{'{id}'}/send-template
{
"template_id": "uuid-del-template",
"variables": {
"1": "Juan Pérez",
"2": "$1,500.00",
"3": "15 de marzo"
}
}
Solo disponible para conversaciones de WhatsApp. Los templates deben estar aprobados por Meta.
Resolver / Cerrar conversación
/v1/conversations/{'{id}'}/resolve
— Marcar como resuelta
/v1/conversations/{'{id}'}/close
— Cerrar definitivamente
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.
MCP Server
Integra Recaudo con asistentes de IA
Recaudo incluye un servidor MCP (Model Context Protocol) que permite a asistentes de IA como Claude Desktop, Cursor, Windsurf y otros clientes MCP interactuar directamente con tu cuenta: gestionar clientes, crear facturas, generar links de pago, administrar suscripciones y más.
Requisitos
- Node.js 18 o superior
- Una Secret Key de la API (
sk_live_osk_test_)
Instalación
cd mcp-server
npm install
npm run build
Configuración en Claude Desktop
Agrega la siguiente configuración en tu archivo claude_desktop_config.json:
{
"mcpServers": {
"recaudo": {
"command": "node",
"args": ["/ruta/a/mcp-server/dist/index.js"],
"env": {
"RECAUDO_API_KEY": "sk_live_tu_secret_key",
"RECAUDO_API_URL": "https://recaudo.app/api/v1"
}
}
}
}
Configuración en Cursor
Crea el archivo .cursor/mcp.json en la raíz de tu proyecto:
{
"mcpServers": {
"recaudo": {
"command": "node",
"args": ["./mcp-server/dist/index.js"],
"env": {
"RECAUDO_API_KEY": "sk_live_tu_secret_key"
}
}
}
}
Herramientas disponibles
El MCP Server expone 69 herramientas organizadas en 11 grupos:
| Grupo | Herramientas | Descripción |
|---|---|---|
| Clientes | 7 | CRUD, métodos de pago, suscripciones |
| Facturas | 9 | CRUD, parcialidades, moratorios |
| Links de Pago | 5 | Crear, cancelar, ver pagos |
| Cargos | 3 | Cobros directos, reembolsos |
| Productos | 10 | Catálogo: categorías y productos con inventario |
| Pedidos | 8 | Crear, enviar, entregar, completar pedidos |
| Conversaciones | 7 | WhatsApp, Messenger, Instagram: mensajes y templates |
| Planes | 3 | Listar y crear planes de suscripción |
| Suscripciones | 7 | Ciclo completo: crear, pausar, cancelar |
| Flujos | 6 | Desplegar flujos conversacionales |
| Webhooks | 4 | Configurar y probar webhooks |
Ejemplo de uso
Una vez configurado, puedes hablar con tu asistente de IA en lenguaje natural:
Usuario: Lista mis clientes con suscripción activa
Claude: Encontré 24 clientes con suscripción activa...
Usuario: Crea un link de pago de $500 para Juan Pérez
Claude: Link creado: https://recaudo.app/pay/abc123...
Usuario: ¿Cuántas facturas pendientes tengo?
Claude: Tienes 12 facturas pendientes por un total de $45,300.00...
El MCP Server usa tu Secret Key para autenticarse. Todas las operaciones respetan los mismos permisos y límites que la API REST.