Primeros pasos Autenticación Manejo de errores Webhooks POST Timbrar CFDI

Documentación API

La API REST de Timbrify te permite integrar facturación electrónica CFDI 4.0 en cualquier sistema. Todas las peticiones van a https://api.timbrify.com/v1 y devuelven JSON.

🚀 Sandbox disponible. Usa https://sandbox.timbrify.com/v1 para probar sin generar CFDIs reales. Las API Keys de sandbox empiezan con tbf_test_.
Instalación rápida
# npm
npm install @timbrify/sdk

# yarn
yarn add @timbrify/sdk

# Python
pip install timbrify

Autenticación

Timbrify usa API Keys en el header Authorization: Bearer <key>. Genera tus keys desde el panel de tu comercio en Configuración → API Keys.

⚠️ Nunca expongas tu API Key en código cliente (browser). Úsala exclusivamente en tu servidor.
// TypeScript / Node.js
const response = await fetch('https://api.timbrify.com/v1/invoices', {
  headers: {
    'Authorization': `Bearer ${process.env.TIMBRIFY_API_KEY}`,
    'Content-Type': 'application/json',
  },
});

Timbrar CFDI

Genera y timbra un Comprobante Fiscal Digital por Internet (CFDI) versión 4.0. El CFDI queda sellado por el PAC y listo para uso fiscal de forma instantánea.

POST /v1/invoices/stamp Timbra un CFDI de ingreso
Parámetros del cuerpo
ParámetroTipoReq.Descripción
emisorobjectDatos del emisor: rfc, nombre, regimenFiscal
receptorobjectDatos del receptor: rfc, nombre, usoCfdi, regimenFiscalReceptor, domicilioFiscal
conceptosarrayArreglo de conceptos. Cada uno con descripcion, cantidad, valorUnitario, claveProdServ, claveUnidad
seriestringNoSerie de la factura (default: "A")
formaPagostringNoClave SAT de forma de pago (ej: "03" = transferencia, "04" = tarjeta)
metodoPagostringNo"PUE" (pago en una sola exhibición) o "PPD" (parcialidades)
sendEmailbooleanNoEnviar CFDI al email del receptor (default: true)
Ejemplo de petición
await client.invoices.stamp({
  emisor: {
    rfc: 'PMS901231L84',
    nombre: 'PEMEX TRANSFORMACION INDUSTRIAL',
    regimenFiscal: '601', // General de Ley
  },
  receptor: {
    rfc: 'GOML8902154P3',
    nombre: 'LAURA GOMEZ MENDOZA',
    usoCfdi: 'G03', // Gastos en general
    regimenFiscalReceptor: '605',
    domicilioFiscal: '06600', // CP
  },
  conceptos: [{
    descripcion: 'Gasolina Magna',
    cantidad: 24.6,
    valorUnitario: 17.16,
    claveProdServ: '15101514',
    claveUnidad: 'LTR',
    impuestos: {
      traslados: [{
        base: 422.14,
        impuesto: '002', // IVA
        tipoFactor: 'Tasa',
        tasaOCuota: '0.160000',
        importe: 67.54,
      }],
    },
  }],
  formaPago: '04', // Tarjeta de crédito
  metodoPago: 'PUE',
});
Respuesta
{
  "success": true,
  "invoice": {
    "id": "inv_01HPQR2X4Y7Z",
    "uuid": "A4F2E918-3D1C-4B2A-9E87-F1234567890A",
    "folio": "TBF-00482",
    "serie": "A",
    "status": "stamped",
    "total": 489.68,
    "subtotal": 422.14,
    "iva": 67.54,
    "pac": "facturama",
    "stamped_at": "2026-02-23T10:30:00.000Z",
    "pdf_url": "https://cdn.timbrify.com/pdf/A4F2E918.pdf",
    "xml_url": "https://cdn.timbrify.com/xml/A4F2E918.xml"
  }
}

Manejo de errores

Timbrify usa códigos HTTP estándar. Los errores devuelven un objeto JSON con code y message descriptivos.

Código HTTPCódigo internoDescripción
200Operación exitosa
400INVALID_RFCEl RFC del receptor no existe o está cancelado en el SAT
400INVALID_CFDIError en la estructura del XML (parámetros inválidos)
401UNAUTHORIZEDAPI Key inválida o expirada
402QUOTA_EXCEEDEDLímite mensual de CFDIs alcanzado. Considera upgradear tu plan
429RATE_LIMITEDDemasiadas peticiones. Ver Rate limits
503PAC_UNAVAILABLEAmbos PACs no disponibles (rarísimo). Reintentar en 30s

Webhooks

Timbrify envía eventos HTTP POST a tu endpoint cuando ocurren cambios en facturas. Todos los webhooks incluyen firma HMAC-SHA256 para verificación.

Eventos disponibles
EventoDescripción
invoice.stampedCFDI timbrado exitosamente
invoice.cancelledCFDI cancelado en el SAT
invoice.email_sentEmail enviado al receptor
invoice.complement_generatedComplemento de pago (REP) generado
qr.scannedQR ticket escaneado por consumidor
Verificar firma HMAC
import { createHmac } from 'crypto';

export function verifyWebhook(payload: string, signature: string, secret: string) {
  const expected = createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return `sha256=${expected}` === signature;
}

// En tu endpoint Fastify / Express:
const isValid = verifyWebhook(
  req.rawBody,
  req.headers['x-timbrify-signature'],
  process.env.TIMBRIFY_WEBHOOK_SECRET
);