🇲🇽 ¿Cómo funciona la Integración de Facturación (CFDI)?
La Facturación de Toku permite emitir y gestionar CFDIs en México de forma automática, a partir de tus pagos y/o deudas, cumpliendo con la normativa fiscal del SAT y sin procesos manuales. Desde un mismo lugar emites facturas (PUE y PPD), complementos de pago, cancelaciones y notas de crédito.
Cómo funciona: el modelo en dos capas
La integración se entiende en dos capas. Primero cargas la información fiscal (Data In) del receptor y de la deuda; después emites el CFDI y recibes el documento timbrado.

Tu rol vs. el de TokuEn la integración vía API tú decides cuándo y qué facturar y llamas directo a los endpoints. Toku actúa como motor de validación y emisión fiscal.
Glosario
| Término | Qué es |
|---|---|
| billing_document | El CFDI como entidad en Toku: tiene id_billing_document, status, type (INVOICE / COMPLEMENT / CREDIT_NOTE), folio, pdf_url, xml_url y el folio fiscal (fiscal_billing_id, el UUID del SAT). |
| Customer Billing Data | Datos fiscales del receptor de la factura (RFC, régimen, razón social, CP). |
| Invoice Billing Data | Datos fiscales de la deuda: uso de CFDI, tipo de pago (PUE/PPD), conceptos (items) e impuestos. |
| PUE | Pago en una sola exhibición. Se factura al momento del pago. Se ancla a una transaction. |
| PPD | Pago en parcialidades o diferido. Se factura sobre la deuda (invoice); el pago se registra después con un complemento. |
| Complemento de pago | Documento que se emite al recibir el pago de una factura PPD. Se ancla a una transaction. |
| Nota de crédito | Documento que corrige o revierte (total o parcialmente) una factura ya emitida. Queda vinculada a ella. |
| id_account | Identificador de la razón social emisora. Es lo que habilita multi razón social. |
1. Antes de empezar
1.1 Autenticación
Todas las llamadas se autentican con tu API Key en el header x-api-key, contra la base URL https://api.trytoku.com.
Por ejemplo:
curl https://api.trytoku.com/customer-billing-data \
-H "x-api-key: TU_API_KEY" \
-H "Content-Type: application/json"1.2 Requisitos fiscales
La facturación se apoya en tu propia infraestructura fiscal. Antes de la primera emisión necesitas:
- Estar registrado en el SAT con un RFC válido.
- Contar con tu Certificado de Sello Digital (CSD): archivos
.cer,.keyy contraseña. - Tener contratado Facturama (el PAC con el que timbra Toku) y contar con folios (timbres) disponibles.
- Configurar estas credenciales junto con el equipo de Toku. Es un setup único; mientras no esté listo, la funcionalidad no se activa.
Folios agotados = emisiones fallidasLas facturas se timbran contra tus folios de Facturama. Si se agotan, las emisiones empezarán a fallar hasta que recargues; contémplalo en tu operación.
1.3 Requisitos previos
La facturación se construye sobre los objetos core de Toku. Antes de facturar, según el caso, deben existir:
- El Customer (
cus_...) — siempre. - La Invoice (
in_...) — siempre. - La Transaction (
trs_...) — para PUE y para complementos de pago.
¿Aún no creas los objetos core?Si aún no creas customers, invoices y transactions en Toku, revisa primero la guía Subir deudas y recibir pagos. Esta guía asume que ya los tienes.
1.4 ¿Qué tipo de CFDI corresponde?
La pregunta clave es en qué momento facturas. Esto define el tipo de CFDI y a qué objeto se ancla la emisión.
| Facturo cuando… | Tipo de CFDI | Se ancla a | Endpoint de emisión |
|---|---|---|---|
| Recibo el pago | PUE | Transaction | POST /billing-documents/pue |
| Se genera la deuda (pago posterior o en parcialidades) | PPD | Invoice | POST /billing-documents/ppd |
| Llega el pago de una PPD ya emitida | Complemento | Transaction | POST /billing-documents/ppd-complement |
PUE vs PPDEl tipo de pago que declaras en Invoice Billing Data (
cfdi_payment_method: PUE o PPD) debe ser coherente con el endpoint de emisión que llamas. Una factura PUE se emite con un solo item; una PPD admite varios.
2. Cargar la información fiscal (Data In)
Antes de emitir cualquier CFDI necesitas cargar los datos fiscales del receptor y de la deuda.
2.1 Información fiscal del Customer (receptor)
Guarda los datos fiscales con los que se emitirán las facturas del Customer.
Crear — POST /customer-billing-data
/customer-billing-data| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| id_customer | string | Identificador único del Customer (cus_...). | 🔘 Sí |
| tax_id | string | RFC del receptor. | 🔘 Sí |
| tax_regime | string | Clave del régimen fiscal según el SAT (ej. 601). | 🔘 Sí |
| legal_name | string | Razón social / nombre exacto registrado ante el SAT. | 🔘 Sí |
| tax_zip_code | string | Código postal fiscal del receptor. | 🔘 Sí |
| billing_address | string | Dirección fiscal. | ⚪ No |
| billing_email | string | Correo al que se enviarán las facturas. | ⚪ No |
Request
Por ejemplo
{
"id_customer": "cus_example1234",
"tax_id": "XAXX010101000",
"tax_regime": "601",
"legal_name": "Empresa Demo SA de CV",
"tax_zip_code": "54054",
"billing_address": "Emerson 180, CDMX",
"billing_email": "[email protected]"
}Consultar — GET /customer-billing-data/{id_customer}
/customer-billing-data/{id_customer}Devuelve la información fiscal cargada para ese Customer. Usa el id_customer como path param.
Actualizar — PATCH /customer-billing-data/{id_customer}
/customer-billing-data/{id_customer}Mismos campos que en la creación (todos opcionales en el body; envías solo los que cambian). Úsalo, por ejemplo, para corregir un régimen o una razón social que no coincide con el SAT.
Datos fiscales válidos ante el SAT
legal_name,tax_regimeycfdi_usedeben ser válidos y coherentes ante el SAT. La mayoría de los errores de emisión vienen de aquí: razón social que no coincide con el RFC, régimen fuera de catálogo o uso de CFDI incompatible con el régimen.
2.2 Información fiscal de la Deuda (invoice)
Define cómo se construye el CFDI de esa deuda: uso, tipo de pago, conceptos e impuestos.
Crear — POST /invoice-billing-data
/invoice-billing-data| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| id_invoice | string | Identificador único de la Invoice (in_...). | 🔘 Sí |
| cfdi_use | string | Uso del CFDI según el SAT (ej. G03, CP01). | 🔘 Sí |
| cfdi_payment_method | string | PUE o PPD. Define el tipo de factura. | 🔘 Sí |
| item | object | Concepto a facturar (ver detalle abajo). | 🔘 Sí |
| observations | string | Observaciones a incluir en la factura. | ⚪ No |
Campos de item
| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| product_code | string | Clave de producto/servicio del SAT. | 🔘 Sí |
| product_description | string | Descripción del producto. | 🔘 Sí |
| unit_code | string | Clave de unidad de medida del SAT. | 🔘 Sí |
| quantity | integer | Cantidad. Default 1. | ⚪ No |
| tax_object | string | Objeto de impuesto del SAT. Default 02. | ⚪ No |
| unit_price | number | Si no se envía, se calcula a partir del monto de la Invoice. (Obligatorio en notas de crédito.) | ⚪ No |
| taxes | array | Impuestos aplicables (ver abajo). Requerido; acepta lista vacía. | 🔘 Sí |
Campos de cada taxes[]
| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| rate | number | Tasa del impuesto (ej. 0.16). | 🔘 Sí |
| is_retention | boolean | Indica si es un impuesto retenido. | 🔘 Sí |
| name | string | IVA, ISR, IEPS, IVA RET o IVA Exento. | 🔘 Sí |
Impuestos y exención
taxeses requerido, pero acepta lista vacía ("taxes": []) cuando el concepto no lleva impuestos. Para una operación exenta, incluye un impuesto conname: "IVA Exento".
Request
Por ejemplo
{
"id_invoice": "in_example1234",
"cfdi_use": "G03",
"cfdi_payment_method": "PPD",
"item": {
"product_code": "81112101",
"product_description": "Servicio mensual",
"unit_code": "E48",
"quantity": 1,
"tax_object": "02",
"unit_price": 200.45,
"taxes": [
{
"rate": 0.16,
"is_retention": false,
"name": "IVA"
}
]
},
"observations": "Observaciones opcionales"
}Consultar — GET /invoice-billing-data/{id_invoice}
/invoice-billing-data/{id_invoice} Devuelve la información fiscal cargada para esa Invoice.
Actualizar — PUT /invoice-billing-data/{id_invoice}
/invoice-billing-data/{id_invoice}Requiere cfdi_use, cfdi_payment_method e item en el body.
2.3 Facturación a público general
Cuando no cuentas con los datos fiscales del Customer pero igual quieres avanzar con la factura, puedes cargar información fiscal genérica (público general) para uno o varios customers a la vez.
Crear — POST /customer-billing-data/general-public/bulk
/customer-billing-data/general-public/bulk| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| id_customers | array<string> | Lista de ids de Customers (mínimo 1, máximo 5000; pasarse devuelve 400). | 🔘 Sí |
Request
{
"id_customers": [
"cus_K38NHOpo35qLUDgFUecLbhADCHX-Tvvn",
"cus_sB7M0mKTUllsc_ec2Tp09fGBVzvH5jMR"
]
}Response
{
"created": ["cus_K38NHOpo35qLUDgFUecLbhADCHX-Tvvn"],
"skipped": [
{ "id_customer": "cus_sB7M0mKTUllsc_ec2Tp09fGBVzvH5jMR", "reason": "Customer already has billing data" }
],
"failed": [],
"summary": { "total_requested": 2, "created_count": 1, "skipped_count": 1, "failed_count": 0 }
}
Respuesta en loteEl endpoint procesa en lote y te devuelve qué customers se crearon (
created), cuáles se omitieron y por qué (skipped), y cuáles fallaron (failed), más unsummary.
3. Emitir el CFDI
Con la información fiscal cargada, disparas la emisión. Todas las respuestas devuelven uno o más objetos billing_document.
3.1 Factura PUE: POST /billing-documents/pue
/billing-documents/pueEmite una factura de pago en una sola exhibición, asociada a una transaction (el pago ya ocurrió).
| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| id_transaction | string | Identificador único de la Transaction. | 🔘 Sí |
| id_account | string | Identificador de la razón social emisora. | 🔘 Sí |
| issue_date | string | Fecha de emisión YYYY-MM-DDTHH:MM:SS. Default: fecha del pago. | ⚪ No |
Request
{
"id_transaction": "trs_jasdkjasndi",
"id_account": "acc_2BrF6x",
"issue_date": "2025-10-02T10:25:00"
}3.2 Factura PPD: POST /billing-documents/ppd
/billing-documents/ppdEmite una factura de pago en parcialidades o diferido, asociada a la deuda (invoice). El pago se registra después con un complemento.
| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| id_invoice | string | Identificador único de la Invoice. | 🔘 Sí |
| id_account | string | Identificador de la razón social emisora. | 🔘 Sí |
| issue_date | string | Fecha de emisión YYYY-MM-DDTHH:MM:SS. Opcional. Default: fecha actual. | ⚪ No |
Request
{
"id_invoice": "in_MxSCCZLVndll",
"id_account": "acc_2BrF6x"
}Response (ejemplo, aplica a PUE y PPD)
[
{
"id_billing_document": "bd_JdDo6bebL",
"id_invoice": "in_MxSCCZLVndll",
"id_transaction": null,
"id_account": "acc_2BrF6x",
"id_customer": "cus_3pVgIaKg",
"status": "ISSUED",
"type": "INVOICE",
"folio": "15",
"issue_date": "2025-10-02T10:25:00",
"sub_total": 100.5,
"discount": 0.0,
"total": 116.58,
"pdf_url": "https://storage.cloud.google.com/.../15.pdf",
"xml_url": "https://storage.cloud.google.com/.../15.xml",
"provider_external_id": "qgzRhubiJ",
"fiscal_billing_id": "a38685b0-99f8-4fb6-9",
"tax_id": "BOOE980",
"tax_regime": "605",
"tax_zip_code": "54054"
}
]3.3 Complemento de pago: POST /billing-documents/ppd-complement
/billing-documents/ppd-complementEmite el complemento que registra el pago de una factura PPD previamente emitida.
| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| id_transaction | string | Identificador único de la Transaction del pago. | 🔘 Sí |
| id_account | string | Identificador de la razón social emisora. | 🔘 Sí |
| issue_date | string | Fecha de emisión YYYY-MM-DDTHH:MM:SS. Opcional. | ⚪ No |
Solo para pagos vía TokuEl complemento está asociado a una transaction, por lo que no se puede generar para deudas pagadas fuera de Toku (no hay transaction que lo respalde).
El objeto billing_document
Es lo que devuelven la emisión, la consulta y los webhooks. Campos principales:
| Campo | Descripción |
|---|---|
| id_billing_document | Id del documento en Toku (bd_...). |
| type | INVOICE, COMPLEMENT o CREDIT_NOTE. |
| status | Estado del documento (ej. ISSUED). |
| folio | Folio interno del documento. |
| fiscal_billing_id | Folio fiscal (UUID del SAT). |
| related_fiscal_billing_id | UUID del documento relacionado (ej. la factura origen de un complemento o nota de crédito). |
| pdf_url / xml_url | Enlaces al PDF y XML timbrados. |
| provider_external_id | Id de la factura en el proveedor fiscal. Se usa como referencia para emitir notas de crédito. |
| id_invoice / id_transaction / id_customer / id_account | Referencias a los objetos relacionados. |
| sub_total / discount / total | Montos. |
| tax_id / tax_regime / tax_zip_code / billing_address / billing_email | Datos fiscales del receptor. |
4. Recibir notificaciones (webhooks)
Toku notifica la emisión de documentos vía webhook. Inscribe un Webhook Endpoint suscrito al evento de facturación y valida la firma Toku-Signature igual que con el resto de eventos (ver la guía de Notificaciones y webhooks).
Evento billing_document.created
billing_document.createdNotifica cuando un documento fiscal ha sido creado. El payload trae el mismo objeto billing_document, incluyendo los enlaces a PDF y XML.
{
"id": "wheve_72s5JQEfl3zJf9rkjhvkhjvhclu",
"event_type": "billing_document.created",
"id_billing_document": "bd_ljhvcgkgv",
"id_invoice": "in_JgPlqLz6Whpr",
"id_transaction": "trs_sMfOXfkoXTyoTK",
"id_customer": "cus_KfRGfQFVO",
"status": "ISSUED",
"type": "INVOICE",
"folio": "5",
"issue_date": "2025-10-01T01:06:47",
"sub_total": 8.62,
"discount": 0.0,
"total": 10.0,
"pdf_url": "https://storage.cloud.google.com/.../doc.pdf",
"xml_url": "https://storage.cloud.google.com/.../doc.xml",
"provider_external_id": "GpEdu_HCzvtldgVDW8aUwg2",
"fiscal_billing_id": "example_uuid",
"related_fiscal_billing_id": null,
"tax_id": "EXAMPLERFC",
"tax_regime": "611",
"tax_zip_code": "27345"
}5. Operar sobre facturas emitidas
5.1 Cancelación — POST /billing-documents/{id_billing_document}/cancel
/billing-documents/{id_billing_document}/cancelSolicita la cancelación de una factura ante el SAT. Usa el id_billing_document como path param (sin body).
Response
{
"success": true,
"message": "Billing document {id} cancelled",
"data": {
"Status": "canceled",
"Message": "Cancelado: Cancelado sin aceptación",
"IsCancelable": "Cancelable sin aceptación",
"Uuid": "uuid",
"RequestDate": "2025-10-23T12:55:00",
"AcuseStatus": 201,
"AcuseStatusDetails": "Solicitud de cancelación recibida."
}
}
Estados de cancelaciónLa cancelación depende del SAT y del tipo de factura. Puede quedar cancelable sin aceptación, o requerir aceptación del receptor (queda pendiente hasta que el receptor responda) y puede ser rechazada. Una cancelación pendiente o rechazada es un estado esperado: no se reintenta. Si el SAT rechaza la cancelación, evalúa emitir una nota de crédito.
5.2 Nota de crédito — POST /billing-documents/credit-note
/billing-documents/credit-noteCorrige o revierte (total o parcialmente) una factura ya emitida. Queda vinculada a la factura origen.
| Nombre | Tipo | Descripción | Requerido |
|---|---|---|---|
| provider_external_id | string | Id de la factura origen en el proveedor (viene en la respuesta de emisión como provider_external_id). | 🔘 Sí |
| payment_form | string | Forma de pago según el SAT. | 🔘 Sí |
| items | array | Concepto(s) de la nota (mínimo 1). Mismo esquema que el item de Invoice Billing Data; aquí unit_price es obligatorio. | 🔘 Sí |
| reason | string | Motivo de la nota. | ⚪ No |
| amount | number | Monto de la nota. | ⚪ No |
| date | string | Fecha en formato ISO. | ⚪ No |
Request
{
"provider_external_id": "GpEdu_HCzvtldgVDW8aUwg2",
"payment_form": "03",
"reason": "Devolución parcial",
"amount": 100,
"items": [
{
"product_code": "81112101",
"product_description": "Servicio mensual",
"unit_code": "E48",
"quantity": 1,
"unit_price": 100,
"tax_object": "02",
"taxes": [
{ "rate": 0.16, "is_retention": false, "name": "IVA" }
]
}
]
}6. Consultar facturas — GET /billing-documents
/billing-documentsDevuelve los documentos fiscales emitidos, con filtros y paginación.
| Parámetro | Tipo | Descripción |
|---|---|---|
| id_invoice | string | Filtra por Invoice. |
| id_transaction | string | Filtra por Transaction. |
| folio | string | Filtra por folio. |
| start_date | date | Desde qué fecha buscar. |
| end_date | date | Hasta qué fecha buscar. |
| status | string | Filtra por estado. |
| limit | integer | Máximo de resultados. Default 20. |
| offset | integer | Desplazamiento para paginar. |
ConciliaciónComo sistema de protección (si algún webhook no llegó), se recomienda una consulta periódica de los documentos emitidos para conciliar contra los eventos recibidos.
7. Buenas prácticas y errores frecuentes
No todos los errores de facturación requieren una acción sobre la integración: muchos vienen de los datos fiscales que cargas o de definiciones del propio negocio. La clave es identificar el origen.
Datos fiscales del customer
| Error | Qué significa | Cómo resolver |
|---|---|---|
| Customer billing data not found | No hay datos fiscales cargados para el customer. | Carga el Customer Billing Data antes de emitir. |
| Uso CFDI no válido para el régimen | El cfdi_use no es compatible con el régimen. | Corrige el uso de CFDI según el régimen del receptor. |
| Régimen fiscal inválido o inexistente | Régimen fuera de catálogo o incorrecto para el RFC. | Corrige tax_regime y actualiza el customer. |
| Nombre del receptor no coincide con RFC | legal_name no coincide con el registrado en el SAT. | Usa la razón social exacta y actualiza el customer. |
Datos de la deuda / invoice
| Error | Qué significa | Cómo resolver |
|---|---|---|
| Invoice billing data not found | La deuda no tiene datos de facturación. | Carga el Invoice Billing Data y reintenta. |
| Input string not in a correct format | Algún campo tiene formato inválido. | Revisa montos y campos vacíos. |
| Tax rate must be greater than zero | Tasa de impuesto inválida. | Corrige los impuestos del item. |
| Tipo de factura incompatible (PUE/PPD) | El endpoint no coincide con el cfdi_payment_method. | Alinea el tipo declarado con el endpoint de emisión. |
Cancelaciones y notas de crédito
| Error | Qué significa | Cómo resolver |
|---|---|---|
| Factura no encontrada / no elegible | El documento no existe o no puede operarse. | Verifica id_billing_document y el estado. |
| Cancelación pendiente | El SAT aún no resuelve (requiere aceptación). | Estado esperado. No reintentar. |
| Cancelación rechazada | El receptor rechazó la cancelación. | Evaluar nota de crédito. No reintentar. |
| Nota de crédito no puede generarse | Factura base inválida (no emitida o ya cancelada). | Verifica estado y UUID de la factura origen. |
Recomendaciones generales
- No dispares dos veces la misma emisión; usa la consulta para verificar si ya existe el documento antes de reintentar.
- Guarda
id_billing_document,fiscal_billing_idyprovider_external_id: los necesitarás para cancelar, emitir complementos y notas de crédito. - Una cancelación pendiente o rechazada no se reintenta.
8. Referencia rápida de endpoints
| Acción | Método | Ruta | Requeridos |
|---|---|---|---|
| Crear billing data customer | POST | /customer-billing-data | id_customer, tax_id, tax_regime, legal_name, tax_zip_code |
| Obtener billing data customer | GET | /customer-billing-data/{id_customer} | id_customer |
| Actualizar billing data customer | PATCH | /customer-billing-data/{id_customer} | id_customer |
| Público general (bulk) | POST | /customer-billing-data/general-public/bulk | id_customers[] |
| Crear billing data invoice | POST | /invoice-billing-data | id_invoice, cfdi_use, cfdi_payment_method, item |
| Obtener billing data invoice | GET | /invoice-billing-data/{id_invoice} | id_invoice |
| Actualizar billing data invoice | PUT | /invoice-billing-data/{id_invoice} | id_invoice, cfdi_use, cfdi_payment_method, item |
| Emitir PUE | POST | /billing-documents/pue | id_transaction, id_account |
| Emitir PPD | POST | /billing-documents/ppd | id_invoice, id_account |
| Emitir complemento | POST | /billing-documents/ppd-complement | id_transaction, id_account |
| Cancelar | POST | /billing-documents/{id_billing_document}/cancel | id_billing_document |
| Nota de crédito | POST | /billing-documents/credit-note | provider_external_id, payment_form, items |
| Consultar facturas | GET | /billing-documents | — (filtros opcionales) |

