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

Response
{
  "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

Response
{
  "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.

POST /v1/customers

Crear Cliente

Crea un nuevo cliente en tu cuenta.

Parametros

Parametro Tipo Requerido Descripcion
email string Email del cliente
name string 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'}
    }
)
Response 201
{
  "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"
  }
}
GET /v1/customers

Listar Clientes

Obtiene una lista paginada de todos los clientes.

Request
curl https://recaudo.app/api/v1/customers?limit=10 \
  -H "Authorization: Bearer sk_live_xxx"
Response 200
{
  "object": "list",
  "data": [
    {
      "id": "cus_a1b2c3d4e5f6",
      "object": "customer",
      "email": "cliente@ejemplo.com",
      "name": "Juan Perez",
      ...
    }
  ],
  "has_more": true,
  "total_count": 150
}
GET /v1/customers/:id

Obtener Cliente

Obtiene los detalles de un cliente existente.

PATCH /v1/customers/:id

Actualizar Cliente

Actualiza los datos de un cliente. Solo se modifican los campos enviados.

DELETE /v1/customers/:id

Eliminar Cliente

Elimina un cliente y sus datos asociados. Esta accion no se puede deshacer.

Cargos Directos

Realiza cargos directos a metodos de pago guardados. Ideal para cargos recurrentes o one-click payments.

POST /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
Request
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"
  }'
Response 201
{
  "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"
}
POST /v1/charges/:id/refund

Reembolsar Cargo

Realiza un reembolso total o parcial de un cargo.

Request (reembolso parcial)
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"
  }'
Response 200
{
  "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.

White-label checkout: Este sistema te permite crear tu propia página de checkout sin necesidad de redirigir a páginas externas. Los datos de tarjeta nunca pasan por tu servidor.

Integración en 4 pasos

1

Incluir la librería JavaScript

HTML
<script src="https://recaudo.app/js/recaudo.min.js"></script>
2

Inicializar la librería

Inicializa recaudo.js con tu Publishable Key. El método init() carga el SDK del procesador configurado.

JavaScript
// 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'
3

Tokenizar la tarjeta

Los datos de la tarjeta se tokenizan directamente con el procesador de pagos. Nunca pasan por tu servidor.

JavaScript
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);
  }
}
4

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 - Cargo directo
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 - Crear suscripción
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)

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).
Seguridad: Los tokens son generados directamente por el procesador de pagos (Conekta/Stripe). Los datos de tarjeta nunca pasan por los servidores de Recaudo ni por tu servidor, facilitando el cumplimiento de PCI DSS.

Manejo de Errores

Los errores de recaudo.js incluyen un tipo para facilitar su manejo:

Ejemplo de manejo de errores
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.

POST /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) ID del cliente
plan string (uuid) 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"
    }
  }'
Response 201
{
  "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

POST /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"}'
POST /v1/subscriptions/:id/pause Pausar suscripcion
curl -X POST https://recaudo.app/api/v1/subscriptions/sub_1a2b3c/pause \
  -H "Authorization: Bearer sk_live_xxx"
POST /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

GET /v1/invoices Listar facturas
POST /v1/invoices Crear factura (con o sin parcialidades)
GET /v1/invoices/:id Obtener factura
PATCH /v1/invoices/:id Actualizar factura
DELETE /v1/invoices/:id Eliminar factura
POST /v1/invoices/:id/pay Marcar como pagada
POST /v1/invoices/:id/void Anular factura
GET /v1/invoices/:id/installments Listar parcialidades
PATCH /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.

Manual Installments Example
{
  "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

Invoice Object with Installments
{
  "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.created
  • payment.succeeded
  • payment.failed
  • payment.refunded

Links de Pago

  • payment_link.created
  • payment_link.paid
  • payment_link.expired

Suscripciones

  • subscription.created
  • subscription.updated
  • subscription.canceled
  • subscription.paused
  • subscription.resumed
  • subscription.trial_ending

Facturas

  • invoice.created
  • invoice.paid
  • invoice.payment_failed
  • invoice.upcoming

Formato del Payload

Webhook 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

GET /v1/product-categories

Parámetros opcionales: is_active, parent_id (UUID o "root"), limit, page

Crear categoría

POST /v1/product-categories
Body
{
  "name": "Electrónicos",
  "description": "Productos electrónicos y gadgets",
  "parent_id": null,
  "sort_order": 1,
  "is_active": true
}
Campo Tipo Requerido Descripción
namestringNombre de la categoría
parent_iduuidNoCategoría padre (para subcategorías)
descriptionstringNoDescripción
sort_orderintegerNoOrden de visualización
is_activebooleanNoSi está activa (default: true)

Obtener / Actualizar / Eliminar categoría

GET /v1/product-categories/{'{id}'}
PATCH /v1/product-categories/{'{id}'}
DELETE /v1/product-categories/{'{id}'}

Al eliminar una categoría, sus subcategorías se mueven al padre y los productos se desvinculan.


Listar productos

GET /v1/products

Parámetros opcionales: status (active, draft, archived), category (UUID), search, featured, in_stock, limit, page

Crear producto

POST /v1/products
Body
{
  "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
namestringNombre del producto
pricenumberPrecio en decimales (100.00 = $100)
currencystringNoMoneda ISO 4217 (default según tenant)
statusstringNoactive, draft, archived (default: active)
product_category_iduuidNoUUID de la categoría
skustringNoCódigo SKU
descriptionstringNoDescripción larga
compare_at_pricenumberNoPrecio de comparación (antes)
cost_pricenumberNoCosto del producto
track_stockbooleanNoActivar control de inventario
stock_quantityintegerNoCantidad en stock
tax_ratenumberNoTasa de impuesto (%)
is_featuredbooleanNoProducto destacado
image_urlurlNoURL de la imagen
metadataobjectNoMetadatos personalizados

Obtener / Actualizar / Eliminar producto

GET /v1/products/{'{id}'}
PATCH /v1/products/{'{id}'}
DELETE /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

GET /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

POST /v1/orders
Body
{
  "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_iduuidUUID del cliente
itemsarrayLista de productos con product_id y quantity
notesstringNoNotas visibles para el cliente
internal_notesstringNoNotas internas
currencystringNoMoneda ISO 4217
shipping_namestringNoNombre del destinatario
shipping_phonestringNoTeléfono del destinatario
shipping_addressstringNoDirección de envío
metadataobjectNoMetadatos personalizados

Obtener pedido

GET /v1/orders/{'{id}'}

Incluye cliente, items con productos y factura asociada.

Ciclo de vida del pedido

POST /v1/orders/{'{id}'}/cancel — Cancelar pedido (restaura stock)
POST /v1/orders/{'{id}'}/ship — Marcar como enviado
POST /v1/orders/{'{id}'}/deliver — Marcar como entregado
POST /v1/orders/{'{id}'}/complete — Marcar como completado

Flujo típico: pending → confirmed → shipped → delivered → completed

Items del pedido

GET /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

GET /v1/conversations

Parámetros opcionales: channel (whatsapp, messenger, instagram), status (open, waiting, resolved, closed), customer (UUID), assigned_to (UUID), limit, page

Obtener conversación

GET /v1/conversations/{'{id}'}

Incluye cliente y agente asignado.

Listar mensajes

GET /v1/conversations/{'{id}'}/messages

Parámetros opcionales: type (text, image, video, audio, document, template), limit, page

Enviar mensaje

POST /v1/conversations/{'{id}'}/messages
Body — Mensaje de texto
{
  "body": "Hola, tu pedido ha sido enviado. Número de guía: ABC123"
}
Body — Mensaje con multimedia
{
  "body": "Aquí está tu factura",
  "media_url": "https://ejemplo.com/factura.pdf",
  "media_type": "document"
}
Campo Tipo Requerido Descripción
bodystringNo*Texto del mensaje (máx 4096 chars)
media_urlurlNo*URL del archivo multimedia
media_typestringNoimage, video, audio, document

* Se requiere al menos body o media_url.

Enviar template de WhatsApp

POST /v1/conversations/{'{id}'}/send-template
Body
{
  "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

POST /v1/conversations/{'{id}'}/resolve — Marcar como resuelta
POST /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.

GET /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"
}
POST /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"
    ]
  }
}
GET /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"
}
GET /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"
}
POST /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"
  }
}
POST /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_ o sk_test_)

Instalación

Terminal
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:

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:

.cursor/mcp.json
{
  "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:

Conversación con Claude

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.