🇲🇽 ¿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 Toku

En 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érminoQué es
billing_documentEl 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 DataDatos fiscales del receptor de la factura (RFC, régimen, razón social, CP).
Invoice Billing DataDatos fiscales de la deuda: uso de CFDI, tipo de pago (PUE/PPD), conceptos (items) e impuestos.
PUEPago en una sola exhibición. Se factura al momento del pago. Se ancla a una transaction.
PPDPago en parcialidades o diferido. Se factura sobre la deuda (invoice); el pago se registra después con un complemento.
Complemento de pagoDocumento que se emite al recibir el pago de una factura PPD. Se ancla a una transaction.
Nota de créditoDocumento que corrige o revierte (total o parcialmente) una factura ya emitida. Queda vinculada a ella.
id_accountIdentificador 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, .key y 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 fallidas

Las 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 CFDISe ancla aEndpoint de emisión
Recibo el pagoPUETransactionPOST /billing-documents/pue
Se genera la deuda (pago posterior o en parcialidades)PPDInvoicePOST /billing-documents/ppd
Llega el pago de una PPD ya emitidaComplementoTransactionPOST /billing-documents/ppd-complement
🚧

PUE vs PPD

El 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

NombreTipoDescripciónRequerido
id_customerstringIdentificador único del Customer (cus_...).🔘 Sí
tax_idstringRFC del receptor.🔘 Sí
tax_regimestringClave del régimen fiscal según el SAT (ej. 601).🔘 Sí
legal_namestringRazón social / nombre exacto registrado ante el SAT.🔘 Sí
tax_zip_codestringCódigo postal fiscal del receptor.🔘 Sí
billing_addressstringDirección fiscal.⚪ No
billing_emailstringCorreo 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}

Devuelve la información fiscal cargada para ese Customer. Usa el id_customer como path param.

Actualizar — PATCH /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_regime y cfdi_use deben 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

NombreTipoDescripciónRequerido
id_invoicestringIdentificador único de la Invoice (in_...).🔘 Sí
cfdi_usestringUso del CFDI según el SAT (ej. G03, CP01).🔘 Sí
cfdi_payment_methodstringPUE o PPD. Define el tipo de factura.🔘 Sí
itemobjectConcepto a facturar (ver detalle abajo).🔘 Sí
observationsstringObservaciones a incluir en la factura.⚪ No

Campos de item

NombreTipoDescripciónRequerido
product_codestringClave de producto/servicio del SAT.🔘 Sí
product_descriptionstringDescripción del producto.🔘 Sí
unit_codestringClave de unidad de medida del SAT.🔘 Sí
quantityintegerCantidad. Default 1.⚪ No
tax_objectstringObjeto de impuesto del SAT. Default 02.⚪ No
unit_pricenumberSi no se envía, se calcula a partir del monto de la Invoice. (Obligatorio en notas de crédito.)⚪ No
taxesarrayImpuestos aplicables (ver abajo). Requerido; acepta lista vacía.🔘 Sí

Campos de cada taxes[]

NombreTipoDescripciónRequerido
ratenumberTasa del impuesto (ej. 0.16).🔘 Sí
is_retentionbooleanIndica si es un impuesto retenido.🔘 Sí
namestringIVA, ISR, IEPS, IVA RET o IVA Exento.🔘 Sí
📘

Impuestos y exención

taxes es requerido, pero acepta lista vacía ("taxes": []) cuando el concepto no lleva impuestos. Para una operación exenta, incluye un impuesto con name: "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}

Devuelve la información fiscal cargada para esa Invoice.

Actualizar — PUT /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

NombreTipoDescripciónRequerido
id_customersarray<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 lote

El 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 un summary.

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

Emite una factura de pago en una sola exhibición, asociada a una transaction (el pago ya ocurrió).

NombreTipoDescripciónRequerido
id_transactionstringIdentificador único de la Transaction.🔘 Sí
id_accountstringIdentificador de la razón social emisora.🔘 Sí
issue_datestringFecha 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

Emite una factura de pago en parcialidades o diferido, asociada a la deuda (invoice). El pago se registra después con un complemento.

NombreTipoDescripciónRequerido
id_invoicestringIdentificador único de la Invoice.🔘 Sí
id_accountstringIdentificador de la razón social emisora.🔘 Sí
issue_datestringFecha 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

Emite el complemento que registra el pago de una factura PPD previamente emitida.

NombreTipoDescripciónRequerido
id_transactionstringIdentificador único de la Transaction del pago.🔘 Sí
id_accountstringIdentificador de la razón social emisora.🔘 Sí
issue_datestringFecha de emisión YYYY-MM-DDTHH:MM:SS. Opcional.⚪ No
❗️

Solo para pagos vía Toku

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

CampoDescripción
id_billing_documentId del documento en Toku (bd_...).
typeINVOICE, COMPLEMENT o CREDIT_NOTE.
statusEstado del documento (ej. ISSUED).
folioFolio interno del documento.
fiscal_billing_idFolio fiscal (UUID del SAT).
related_fiscal_billing_idUUID del documento relacionado (ej. la factura origen de un complemento o nota de crédito).
pdf_url / xml_urlEnlaces al PDF y XML timbrados.
provider_external_idId de la factura en el proveedor fiscal. Se usa como referencia para emitir notas de crédito.
id_invoice / id_transaction / id_customer / id_accountReferencias a los objetos relacionados.
sub_total / discount / totalMontos.
tax_id / tax_regime / tax_zip_code / billing_address / billing_emailDatos 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

Notifica 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

Solicita 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ón

La 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

Corrige o revierte (total o parcialmente) una factura ya emitida. Queda vinculada a la factura origen.

NombreTipoDescripciónRequerido
provider_external_idstringId de la factura origen en el proveedor (viene en la respuesta de emisión como provider_external_id).🔘 Sí
payment_formstringForma de pago según el SAT.🔘 Sí
itemsarrayConcepto(s) de la nota (mínimo 1). Mismo esquema que el item de Invoice Billing Data; aquí unit_price es obligatorio.🔘 Sí
reasonstringMotivo de la nota.⚪ No
amountnumberMonto de la nota.⚪ No
datestringFecha 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

Devuelve los documentos fiscales emitidos, con filtros y paginación.

ParámetroTipoDescripción
id_invoicestringFiltra por Invoice.
id_transactionstringFiltra por Transaction.
foliostringFiltra por folio.
start_datedateDesde qué fecha buscar.
end_datedateHasta qué fecha buscar.
statusstringFiltra por estado.
limitintegerMáximo de resultados. Default 20.
offsetintegerDesplazamiento para paginar.
📘

Conciliación

Como 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

ErrorQué significaCómo resolver
Customer billing data not foundNo hay datos fiscales cargados para el customer.Carga el Customer Billing Data antes de emitir.
Uso CFDI no válido para el régimenEl 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 inexistenteRégimen fuera de catálogo o incorrecto para el RFC.Corrige tax_regime y actualiza el customer.
Nombre del receptor no coincide con RFClegal_name no coincide con el registrado en el SAT.Usa la razón social exacta y actualiza el customer.

Datos de la deuda / invoice

ErrorQué significaCómo resolver
Invoice billing data not foundLa deuda no tiene datos de facturación.Carga el Invoice Billing Data y reintenta.
Input string not in a correct formatAlgún campo tiene formato inválido.Revisa montos y campos vacíos.
Tax rate must be greater than zeroTasa 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

ErrorQué significaCómo resolver
Factura no encontrada / no elegibleEl documento no existe o no puede operarse.Verifica id_billing_document y el estado.
Cancelación pendienteEl SAT aún no resuelve (requiere aceptación).Estado esperado. No reintentar.
Cancelación rechazadaEl receptor rechazó la cancelación.Evaluar nota de crédito. No reintentar.
Nota de crédito no puede generarseFactura 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_id y provider_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ónMétodoRutaRequeridos
Crear billing data customerPOST/customer-billing-dataid_customer, tax_id, tax_regime, legal_name, tax_zip_code
Obtener billing data customerGET/customer-billing-data/{id_customer}id_customer
Actualizar billing data customerPATCH/customer-billing-data/{id_customer}id_customer
Público general (bulk)POST/customer-billing-data/general-public/bulkid_customers[]
Crear billing data invoicePOST/invoice-billing-dataid_invoice, cfdi_use, cfdi_payment_method, item
Obtener billing data invoiceGET/invoice-billing-data/{id_invoice}id_invoice
Actualizar billing data invoicePUT/invoice-billing-data/{id_invoice}id_invoice, cfdi_use, cfdi_payment_method, item
Emitir PUEPOST/billing-documents/pueid_transaction, id_account
Emitir PPDPOST/billing-documents/ppdid_invoice, id_account
Emitir complementoPOST/billing-documents/ppd-complementid_transaction, id_account
CancelarPOST/billing-documents/{id_billing_document}/cancelid_billing_document
Nota de créditoPOST/billing-documents/credit-noteprovider_external_id, payment_form, items
Consultar facturasGET/billing-documents— (filtros opcionales)