MX
Referencia de la API

API de Autofactura

Integra tu sistema POS, ERP o e-commerce con FacturaMX para registrar tickets de venta y permitir que tus clientes generen su CFDI 4.0 al instante.

CFDI 4.0 REST / JSON Bearer Token 60 req/min

Inicio rápido

En menos de 5 minutos puedes enviar tu primer ticket desde cualquier sistema.

1

Obtén tu API Key

En tu dashboard → Autofactura → tab API Externa → Generar API Key.

2

Envía el ticket

Haz un POST a /api/autofactura_webhook.php con los datos de la venta.

3

El cliente factura

Tu cliente recibe el enlace del portal y genera su CFDI 4.0 con sus datos fiscales.

Base URL

https://facturamx.bradown.com/api/autofactura_webhook.php Solo acepta POST

Autenticación

Todas las peticiones deben incluir el header Authorization con tu API Key.

// Header requerido en cada petición
Authorization: Bearer afk_tu_api_key_aqui
Content-Type:  application/json

Seguridad de la API Key

  • Nunca expongas tu API Key en código frontend (JS del navegador).
  • Las llamadas deben hacerse desde tu servidor backend.
  • Puedes regenerar la key en cualquier momento desde el dashboard.
  • Se requieren mínimo 199 timbres en tu saldo para usar la API.

Rate limiting

La API aplica dos niveles de control de tráfico para proteger el servicio.

Por API Key

60 req/min

Se reinicia cada minuto calendario.

Por IP (fallos de auth)

20 fallos / 10 min

La IP queda bloqueada temporalmente.

Headers de respuesta

X-RateLimit-Limit:     60
X-RateLimit-Remaining: 45
X-RateLimit-Reset:     1711234560  // Unix timestamp

Autofactura (Portal)

Webhook POS / ERP

Registra tickets de venta desde tu POS o ERP para que el cliente pueda generar su factura en el portal de autofactura sin intervención manual.

Registrar ticket

POST

Registra una venta en el sistema. El cliente podrá entrar al portal de autofactura, buscar su ticket por código y generar su CFDI 4.0 con sus datos fiscales.

Endpoint

POST /api/autofactura_webhook.php
El ticket registrado no genera ni timbra el CFDI. Solo almacena la venta. El timbrado ocurre cuando el cliente llena sus datos fiscales en el portal de autofactura.
Si envias el mismo ticket otra vez y todavia no ha sido facturado, la API lo toma como actualizacion del ticket. Esto sirve para cambios por devoluciones parciales, cambios de cantidad o correcciones del POS.

Vigencia y depuracion automatica de tickets

Los tickets sin facturar se eliminan automaticamente al cumplir 45 dias desde la fecha de compra. El dashboard muestra alerta visual cuando faltan 7 dias o menos para el vencimiento.

Actualizar ticket

No existe un endpoint distinto para editar. Para actualizar un ticket pendiente, vuelve a hacer POST /api/autofactura_webhook.php con el mismo valor de ticket y manda el estado final correcto de la venta.

Ejemplo POS: se venden 10 articulos y luego regresan 2.

El POS debe reenviar el mismo ticket con las cantidades ya ajustadas a 8. La respuesta incluira "mode": "updated" o "mode": "reactivated" si el ticket estaba cancelado.

Cancelar ticket

POST

Endpoint

POST /api/autofactura_ticket_cancel.php
{
  "ticket": "TX-0001",
  "motivo": "Devolucion total o cancelacion en POS"
}

Consultar ticket

GET / POST

Endpoint

GET /api/autofactura_ticket_get.php?ticket=TX-0001

Devuelve el estado actual del ticket: pending, cancelled o invoiced.

Request body

Campo Tipo Requerido Descripción
ticket string Código único de la venta. Máx. 100 chars. Ej: TX-0001
fecha string Fecha de la venta en formato YYYY-MM-DD. Debe estar dentro del periodo permitido para el negocio.
conceptos array Array de ítems de la venta. Mínimo 1, máximo 100.

Estructura Interna del Array "conceptos" (Campos por Producto/Servicio)

Cada objeto dentro del array conceptos representa un producto o servicio de la venta.

Estos son los campos reales que recibe /api/autofactura_webhook.php. Los alias indicados tambien son aceptados por la API.

Campo Tipo Req. Default Descripción
descripcion string Descripción del producto o servicio.
clave_sat string Clave SAT de producto o servicio (8 dígitos). Ej: 81111508. Acepta también clave_prod_serv.
clave_unit string Clave de unidad SAT. Ej: E48. Acepta también clave_unidad.
unidad_desc string No "Pieza" Descripción visual de la unidad de medida. Ej: Servicio.
cantidad number Cantidad a facturar (permite decimales).
precio_unitario number Precio unitario antes de impuestos.
objeto_imp string No "02" Objeto de impuesto CFDI. Valores aceptados: "01", "02", "03".
iva_tasa number No 0.16 Tasa de IVA. Acepta 0.16, 0.08, 0 o "exento".
ieps_val number No 0.0 Valor del IEPS (tasa o cuota fija).
ieps_type string No "tasa" Tipo de IEPS. Valores: "tasa" o "cuota".
ret_iva_rate number No 0.0 Tasa de retención de IVA en decimales. Ej: 0.1067.
ret_isr_rate number No 0.0 Tasa de retención de ISR en decimales. Ej: 0.1000.

Respuesta exitosa 200

{
  "success": true,
  "ticket_id": 1042,
  "mode": "created",
  "portal_url": "https://tudominio.com/facturas/autofactura/?t=TOKEN&ticket=TX-0001",
  "request_id": "A3F9B12C"
}
Campo Descripción
successtrue siempre en respuesta 200.
ticket_idID interno del ticket en FacturaMX. Guárdalo para referencias futuras.
modeIndica si el ticket fue created, updated o reactivated.
portal_urlURL directa al portal de autofactura con el ticket precargado. Puedes enviarsela al cliente por WhatsApp, email o SMS. Recuerda facturarlo antes de 45 dias para evitar depuracion automatica.
request_idID único de la petición. Incluirlo al reportar un problema para diagnóstico rápido.

Respuesta de error

{
  "success":    false,
  "error":      "API Key inválida o inexistente.",
  "code":       "AUTH_INVALID",
  "field":      "",           // campo inválido, si aplica
  "request_id": "B7D2A91F"
}
Guarda siempre el request_id en tus logs. Con él podemos rastrear cualquier petición en segundos.

Códigos de error

HTTP code Descripción
200 Ticket registrado correctamente.
400 JSON_INVALID El JSON no es valido.
422 MISSING_FIELD Falta un campo requerido. Ver field en la respuesta.
422 INVALID_FORMAT La fecha, clave SAT u otro campo no tiene el formato esperado.
422 DATE_OUT_OF_RANGE La fecha de venta esta fuera del periodo permitido para el negocio.
422 TOO_MANY_ITEMS El array conceptos supera el maximo de 100 items.
422 INVALID_VALUE Cantidad o precio_unitario fuera de rango.
422 INVALID_SAT_CLAVE La clave SAT no existe en el catalogo SAT.
422 STAMP_FAILED El SAT / Finkok rechazo el timbrado. Ver campo finkok_code para el codigo especifico.
404 TICKET_NOT_FOUND El ticket no existe para esa API Key / negocio.
409 DUPLICATE_TICKET El ticket ya fue facturado y ya no puede modificarse.
409 TICKET_ALREADY_INVOICED El ticket ya fue facturado y no puede cancelarse.
401 AUTH_MISSING No se envió el header Authorization.
401 AUTH_INVALID La API Key no existe o no pertenece a ningún negocio activo.
402 INSUFFICIENT_STAMPS Saldo insuficiente para usar la API. El campo error indica el mínimo requerido y tu saldo actual. Ej: "Minimo requerido: 500. Saldo actual: 120."
429 RATE_LIMIT_EXCEEDED Se superaron las 60 peticiones por minuto.
429 IP_BLOCKED IP bloqueada temporalmente por demasiados fallos de auth.
500 INTERNAL_ERROR Error interno del servidor. Reintenta en unos segundos.

Códigos de error Finkok (finkok_code)

Cuando code: "STAMP_FAILED", la respuesta incluye el campo finkok_code con el código exacto que devolvió el PAC. Úsalo para distinguir el tipo de rechazo sin parsear el texto de error.

{
  "success":     false,
  "error":       "[301] El RFC del receptor no está registrado en el SAT.",
  "code":        "STAMP_FAILED",
  "finkok_code": "301",
  "request_id": "X920A1BD"
}
finkok_code Causa Accion sugerida
301 RFC del emisor o receptor invalido / no registrado en el SAT. Verifica que el RFC sea correcto y este activo en el SAT.
302 Fecha de emision fuera del rango permitido por el SAT. Ajusta la fecha al dia actual o al periodo vigente.
303 El XML tiene errores de estructura o namespace. Revisa que los campos del CFDI coincidan con el catalogo SAT.
304 Certificado (CSD) no valido o vencido. Renueva el CSD de la empresa en el SAT y actualiza las credenciales.
305 El sello digital no corresponde al certificado. Verifica que la llave .key y el .cer sean del mismo par.
307 El folio fiscal (UUID) ya fue utilizado. Genera un nuevo CFDI; no reutilizes el mismo UUID.
401 El CFDI contiene errores de calculo o impuestos inconsistentes. Revisa tasas de IVA, IEPS y retenciones en los conceptos.
709 Error interno del PAC (Finkok). Reintento recomendado. Reintenta la solicitud en unos segundos.

* Esta tabla cubre los códigos más frecuentes. Cuando finkok_code no aparezca en esta lista, el campo error contiene el mensaje completo del PAC.

API Directa

Nuevo

Timbra CFDIs al instante, descarga XML/PDF, cancela comprobantes y mantén tus clientes sincronizados directamente desde tu backend con Bearer Token.

Facturacion Directa

POST

Genera y timbra al instante un CFDI 4.0 utilizando la informacion del cliente y la empresa. Este endpoint consume timbres inmediatamente al emitirse con exito.

Tipos de CFDI soportados

Ingreso (I) — Default

Facturas de venta con IVA 16%, 0%, exento o sin impuesto. Soporta Publico en General.

Egreso (E) — Nota de credito

Requiere uuid_relacionado con el UUID de la factura original.

Nomina (N)

Requiere el modulo de Nomina del dashboard. No disponible en este endpoint.

Traslado (T) / Carta Porte

Requiere datos de transporte del Complemento Carta Porte 3.1. No disponible en este endpoint.

Complemento de Pago (PPD)

El complemento de pago tiene su propio endpoint. Aqui solo se emite el CFDI inicial.

Retenciones e Informacion de Pagos

Estructura XML independiente. Usa el modulo de Retenciones del dashboard.

Endpoint

POST /api/facturar_directo.php

Payload (JSON)

Campo Tipo Req. Descripcion
cliente_id int No ID del cliente registrado en el sistema. Requerido si no se usa es_publico_general.
es_publico_general bool No Establece true para emitir a Publico en General (RFC: XAXX010101000).
es_borrador bool No Default false. Si se envia true, se guarda como borrador.
serie / folio string No Serie (ej: "A") y Folio (ej: "102").
moneda string No Default "MXN". Codigo de moneda ISO de 3 caracteres.
tipo_cambio number Cond. Obligatorio si moneda no es MXN (debe ser > 0). Lo exige el SAT para el atributo TipoCambio. Ej: 17.50.
uso_cfdi string Recom. Uso CFDI del receptor. Default "S01" (Sin efectos fiscales). Para clientes registrados normalmente usa "G01", "G03", etc. Debe ser compatible con el Régimen Fiscal del receptor o el SAT rechaza el CFDI. En Público en General se fuerza a S01 automáticamente.
tipo_comprobante string No Default "I". Para nota de credito usa "E" y envia uuid_relacionado.
metodo_pago string No Default "PUE" (Pago en una sola exhibicion) o "PPD".
forma_pago string No Default "03" (Transferencia). Si metodo de pago es PPD, debe ser "99".
exportacion string No Default "01". Clave de exportacion CFDI.
uuid_relacionado array No Lista de UUID relacionados. Requerido cuando tipo_comprobante es "E".
tipo_relacion string No Clave de relación del nodo CfdiRelacionados. Default "04" (Sustitución). Para nota de crédito usa "01".
ticket string No Codigo del ticket registrado en autofactura (ej: "TX-0001"). Si se envia y el timbrado es exitoso, el sistema marca automaticamente ese ticket como facturado para que el cliente ya no pueda volver a facturarlo desde el portal de autofactura. Recomendado cuando el POS tambien usa el modulo de autofactura.
periodicidad string No* Solo Publico en General. "01"=Diario, "02"=Semanal, "03"=Quincenal, "04"=Mensual (default), "05"=Bimestral.
meses string No* Solo Publico en General. Mes del CFDI global en 2 digitos. Ej: "06"=Junio. Default: mes actual.
anio_global string No* Solo Publico en General. Ano del CFDI global (4 digitos). Ej: "2025". Default: ano actual.
conceptos array Si Lista de productos/servicios (mín. 1, máx. 100). Ver la tabla de campos del concepto abajo.

Campos de cada concepto (array conceptos)

Mismos campos y alias que acepta el webhook de autofactura. Los marcados son obligatorios por el SAT.

Campo Tipo Req. Default Descripción
descripcion string Descripción del producto o servicio. Si va vacía, el concepto se ignora.
clave_sat string Clave SAT ProdServ (8 díg.). Se valida contra el catálogo. Alias: clave_prod_serv.
clave_unit string Clave SAT de unidad. Se valida contra el catálogo. Alias: clave_unidad.
precio number Precio unitario antes de impuestos. Alias: precio_unitario.
cantidad number No 1 Cantidad a facturar (permite decimales).
unidad_desc string No "Pieza" Descripción visual de la unidad.
descuento number No 0 Importe de descuento del renglón (no porcentaje). Alias: descuento_row.
objeto_imp string No "02" Objeto de impuesto CFDI. "01" No objeto, "02" Sí objeto, "03" Sí no obligado. Con "02" el concepto debe llevar al menos un impuesto (IVA, IEPS o retención), incluido IVA exento.
iva_tipo string No "tasa" "tasa" o "exento". Alias: iva_type.
iva_tasa number No 0.16 Tasa de IVA: 0.16, 0.08 o 0. Ignorado si iva_tipo="exento". Alias: iva_rate.
ieps_val number No 0 Valor de IEPS (tasa o cuota fija). Solo se aplica si > 0.
ieps_type string No "tasa" "tasa" o "cuota". Alias: ieps_tipo.
ret_iva_rate number No 0 Tasa de retención de IVA en decimales. Ej: 0.1067.
ret_isr_rate number No 0 Tasa de retención de ISR en decimales. Ej: 0.1000.
ret_ieps number No 0 Tasa de retención de IEPS en decimales.

Reglas SAT que validan el timbrado

  • uso_cfdi debe ser compatible con el Régimen Fiscal del receptor. Ej: G03 exige regímenes como 601, 603, 606… Si no concuerdan, el CFDI se rechaza.
  • Con objeto_imp="02" (default) cada concepto debe llevar al menos un impuesto. Si el producto no causa IVA, usa iva_tipo="exento" o cambia a objeto_imp="01".
  • Moneda distinta de MXN requiere tipo_cambio > 0.
  • Método de pago PPD obliga forma de pago "99".

Ejemplo — Cliente registrado (IVA 16%)

{
  "cliente_id": 42,
  "serie": "A",
  "folio": "1024",
  "moneda": "MXN",
  "tipo_comprobante": "I",
  "metodo_pago": "PUE",
  "forma_pago": "03",
  "uso_cfdi": "G03",
  "exportacion": "01",
  "conceptos": [
    {
      "descripcion": "Desarrollo de Software Integrado",
      "clave_sat": "81111508",
      "clave_unit": "E48",
      "cantidad": 1,
      "precio": 15000.00,
      "objeto_imp": "02",
      "iva_tipo": "tasa",
      "iva_tasa": 0.16
    }
  ]
}

Ejemplo — Publico en General (CFDI Global)

Reglas SAT — Publico en General

  • RFC receptor: XAXX010101000 — el sistema lo asigna automaticamente, no lo envies.
  • Uso CFDI S01 y Regimen receptor 616 — asignados automaticamente.
  • Usa "forma_pago": "99" (Por definir) cuando consolidas ventas de un periodo.
  • Los campos periodicidad, meses y anio_global son requeridos por el SAT para el nodo cfdi:InformacionGlobal.
{
  "es_publico_general": true,
  "serie": "PG",
  "folio": "50",
  "metodo_pago": "PUE",
  "forma_pago": "99",
  "periodicidad": "04",
  "meses": "06",
  "anio_global": "2025",
  "conceptos": [
    {
      "descripcion": "Ventas del periodo",
      "clave_sat": "01010101",
      "clave_unit": "ACT",
      "cantidad": 1,
      "precio": 500.00,
      "iva_tipo": "tasa",
      "iva_tasa": 0.16
    }
  ]
}

Respuesta Exitosa (200)

{
  "success":       true,
  "uuid":          "CE890A12-C84F-45F2-81D2-90A091FB7D42",
  "message":       "Factura timbrada con exito",
  "ticket_marked": true,  // solo presente si se envio "ticket" en el payload
  "request_id":   "F39D0A82"
}

¿Como obtener el PDF?

Guarda el uuid de la respuesta y usalo en el endpoint Descargar XML/PDF para obtener el PDF o XML cuando el cliente lo solicite. El PDF se genera con la plantilla configurada por el negocio.

Vista Previa PDF

POST

Genera la representacion en PDF de un comprobante como borrador/vista previa sin consumir timbres ni persistir datos en el servidor.

Endpoint

POST /api/factura_preview.php?format=json

El payload JSON de entrada y parametros de conceptos es exactamente el mismo que el del endpoint de Facturacion Directa.

Parametros Query

Parametro Req. Valores Descripcion
format No json | pdf Default "json". Si se especifica pdf, descarga/abre directamente el PDF binario en el navegador.

Respuesta Exitosa (JSON por defecto)

{
  "success": true,
  "pdf": "JVBERi0xLjQKJVR5cGVzZXR0aW5nIEJ5...", // Base64 string del PDF
  "request_id": "P830D98F"
}

Descargar XML/PDF

GET

Recupera el XML timbrado y genera el PDF de una factura usando el UUID obtenido al timbrar. El PDF se genera con la plantilla configurada por el negocio (la misma que aparece en el dashboard). Requiere el mismo Bearer Token de la API.

PDF binario

?uuid={UUID}&format=pdf

Descarga el PDF directo. Adjuntalo a un correo o muéstralo en un iframe.

XML timbrado

?uuid={UUID}&format=xml

Descarga el XML con el sello SAT. Requerido para declaraciones fiscales.

JSON (ambos en Base64)

?uuid={UUID}&format=json

Default. Devuelve PDF y XML en Base64 dentro de un JSON. Util para procesar en tu servidor.

Endpoint

GET /api/descargar_comprobante.php?uuid={UUID}&format=pdf|xml|json

Parametros Query

Parametro Req. Valores Descripcion
uuid Si* string UUID SAT del comprobante. Lo recibes en la respuesta de facturar_directo. Recomendado sobre id.
id Si* int ID numerico interno (alternativa al UUID). Usa uno u otro.
format No json | xml | pdf Default "json" — devuelve PDF + XML en Base64. pdf descarga el PDF; xml descarga el XML timbrado.

Respuesta cuando format=json (default)

{
  "success": true,
  "uuid":    "CE890A12-C84F-45F2-81D2-90A091FB7D42",
  "xml":    "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4...", // Base64
  "pdf":    "JVBERi0xLjQKJVR5cGVzZXR0aW5nIEJ5IERvbXBkZg...",         // Base64
  "request_id": "E83A9201"
}

Cancelar CFDI (SAT)

POST

Solicita la cancelacion oficial de un CFDI timbrado directamente ante el SAT mediante Finkok.

Endpoint

POST /api/cancelar_cfdi.php

Payload (JSON)

Campo Tipo Req. Descripcion
uuid / id string | int Si El SAT UUID o ID numerico del CFDI que deseas cancelar.
motivo string No Default "02". Codigos del SAT: "01" (Con errores, con relacion), "02" (Con errores, sin relacion), "03" (No se llevo a cabo), "04" (Operacion global nominativa).
uuid_sustitucion string No Requerido unicamente si el motivo es "01". Indica el UUID del nuevo CFDI sustituto.

Respuesta Exitosa (200)

{
  "success": true,
  "message": "Factura cancelada exitosamente ante el SAT.",
  "status_code": "201",
  "uuid": "CE890A12-C84F-45F2-81D2-90A091FB7D42",
  "acuse_url": "storage/uploads/acuses/acuse_e8a93...xml",
  "sat_estado": "vigente",
  "request_id": "K28D9A83"
}

Saldo de Timbres

GET

Consulta el saldo de timbres disponibles en tu negocio. Se requieren mínimo 199 timbres para usar la API.

Endpoint

GET /api/saldo_timbres.php

Respuesta Exitosa (200)

{
  "success":          true,
  "saldo":            1250,
  "min_required_api": 500,
  "negocio":          "Mi Negocio SA de CV",
  "request_id":      "L930F829"
}
Campo Descripción
saldo Timbres disponibles actualmente en tu negocio.
min_required_api Mínimo requerido para usar la API. Actualmente: 199 timbres.
negocio Nombre comercial del negocio asociado a la API Key.

Saldo mínimo requerido: 199 timbres

Si tu saldo cae por debajo de ese número, la API devuelve 402 en todas las peticiones hasta que recargues timbres.

Sincronizar Cliente

POST

Crea o actualiza de manera inteligente los datos fiscales de un cliente utilizando su RFC como clave unica. Si el RFC ya esta registrado en tu negocio, sus datos se actualizaran. Si no existe, se creara un nuevo registro.

Endpoint

POST /api/clientes_sync.php

Payload (JSON)

Campo Tipo Req. Descripcion
nombre_razon_social string Si Nombre completo o Razon Social exacta como consta en la CSF del SAT.
rfc string Si RFC valido (12 o 13 caracteres).
codigo_postal string No Codigo Postal del domicilio fiscal del receptor.
regimen_fiscal string No Codigo de regimen fiscal SAT del receptor (ej: "601", "605", "626", etc.).
email string No Correo electronico del cliente para envio de comprobantes.
{
  "nombre_razon_social": "COCA COLA FEMSA SAB DE CV",
  "rfc": "KOF0112111A1",
  "codigo_postal": "06700",
  "regimen_fiscal": "601",
  "email": "facturacion@femsa.com.mx"
}

Respuesta Exitosa (200)

{
  "success": true,
  "cliente_id": 128,
  "action": "created",
  "message": "Cliente sincronizado exitosamente.",
  "request_id": "M830A91D"
}

Listado / Historial de Facturas

GET

Consulta e investiga el historial de facturas emitidas por tu negocio. Permite filtrar por RFC de cliente, rango de fechas y estatus, con paginación integrada.

Endpoint

GET /api/facturas_list.php

Parámetros Query

Parámetro Tipo Req. Descripción
rfc_cliente string No Filtra facturas emitidas a un RFC específico.
fecha_inicio string No Filtra facturas desde esta fecha (formato YYYY-MM-DD).
fecha_fin string No Filtra facturas hasta esta fecha (formato YYYY-MM-DD).
estatus string No Valores permitidos: "timbrada" o "cancelada".
limit int No Límite de resultados (1 a 100, default: 50).
offset int No Número de registros a omitir para paginación (default: 0).

Respuesta Exitosa (200)

{
  "success": true,
  "invoices": [
    {
      "id": 1024,
      "uuid": "CE890A12-C84F-45F2-81D2-90A091FB7D42",
      "folio": "154",
      "serie": "A",
      "fecha": "2026-03-21",
      "subtotal": 100.00,
      "iva": 16.00,
      "ret_isr": 0.00,
      "ret_iva": 0.00,
      "total": 116.00,
      "estatus": "timbrada",
      "cliente_rfc": "XAXX010101000",
      "cliente_nombre": "PUBLICO EN GENERAL"
    }
  ],
  "limit": 50,
  "offset": 0,
  "request_id": "R920A18D"
}

Estado SAT (UUID)

GET

Consulta el estado de un CFDI directamente en el SAT vía Finkok. Útil para confirmar si una cancelación fue aceptada por el receptor, verificar que un UUID sigue vigente antes de emitir una nota de crédito, o detectar facturas canceladas en el SAT que el sistema aún marca como timbradas.

Endpoint

GET /api/sat_status.php?uuid={UUID}

Parámetros Query

Parámetro Tipo Req. Descripción
uuid string UUID del CFDI a consultar. Debe pertenecer a tu negocio y estar timbrado.

Campos de la Respuesta

Campo Tipo Descripción
sat_estado string Estado que reporta el SAT: "vigente" o "cancelado". Cadena vacía si el SAT no respondió.
es_cancelable string "Cancelable sin aceptacion", "Cancelable con aceptacion", "No cancelable", o cadena vacía.
estatus_local string Estado en tu sistema: "timbrada" o "cancelada".
sincronizado bool | null true si el SAT y tu sistema coinciden. false si están desincronizados. null si el SAT no respondió.
nota string? Presente solo cuando sincronizado = false. Mensaje orientativo sobre la discrepancia.

Respuesta Exitosa (200)

{
  "success": true,
  "uuid": "CE890A12-C84F-45F2-81D2-90A091FB7D42",
  "sat_estado": "vigente",
  "es_cancelable": "Cancelable sin aceptacion",
  "estatus_local": "timbrada",
  "sincronizado": true,
  "request_id": "R920A18D"
}

⚠ Nota sobre sincronizado = false

Si sat_estado = "cancelado" pero estatus_local = "timbrada", el SAT ya procesó la cancelación pero tu sistema no fue actualizado. Esto sucede cuando la cancelación fue aceptada en el portal del SAT directamente sin pasar por la API. En ese caso, usa el endpoint Cancelar CFDI para sincronizar el estatus.

Complemento de Pago (REP)

POST

Genera y timbra un Recibo Electrónico de Pago 2.0 para liquidar uno o más CFDIs emitidos con metodo_pago=PPD. Obligatorio ante el SAT cuando se recibe el pago de una factura PPD.

¿Cuándo emitir el REP?

Cuando emites una factura con metodo_pago=PPD (pago en parcialidades o diferido), el SAT te obliga a emitir este complemento cada vez que recibes un pago. El complemento acredita el saldo pagado y actualiza el saldo insoluto de cada CFDI relacionado.

Endpoint

POST /api/complemento_pago_ext.php

Payload (JSON)

Campo Tipo Req. Descripción
fecha_pagostringFecha y hora del pago. Formato YYYY-MM-DDTHH:ii:ss. Ej: "2025-06-04T14:30:00".
forma_pagostringClave SAT de la forma de pago. Ej: "03" transferencia, "01" efectivo, "04" tarjeta crédito.
invoicesarrayLista de CFDIs que se están pagando (mín. 1). Ver tabla de campos de invoice abajo.
cliente_idintNo*ID interno del receptor. Si se omite, se resuelve automáticamente desde invoices[0].uuid.
monedastringNoMoneda del pago. Default "MXN".
tipo_cambionumberCond.Obligatorio si moneda ≠ MXN.
num_operacionstringNoNúmero de referencia o folio bancario.
rfc_banco_ordstringNoRFC del banco ordenante (del que sale el dinero).
cta_ordstringNoCuenta ordenante. Obligatoria si rfc_banco_ord = XEXX010101000 (banco extranjero).
rfc_banco_benstringNoRFC del banco beneficiario (al que llega el dinero).
cta_benstringNoCuenta beneficiaria. Obligatoria si rfc_banco_ben = XEXX010101000.

Campos de cada invoice

Campo Tipo Req. Descripción
uuidstringUUID del CFDI PPD original que se está pagando.
imp_saldo_antnumberSaldo insoluto anterior (total del CFDI si es la primera parcialidad, o el saldo restante).
imp_pagadonumberImporte que se paga en esta ocasión (≤ imp_saldo_ant).
seriestringNoSerie del CFDI original.
foliostringNoFolio del CFDI original.

Ejemplo — Pago único de una factura

{
  "fecha_pago":    "2025-06-04T14:30:00",
  "forma_pago":    "03",
  "num_operacion": "OP-789456",
  "invoices": [
    {
      "uuid":         "CE890A12-C84F-45F2-81D2-90A091FB7D42",
      "imp_saldo_ant": 11600.00,
      "imp_pagado":    11600.00
    }
  ]
}

Respuesta Exitosa (200)

{
  "success":     true,
  "uuid":        "AA11BB22-CC33-DD44-EE55-FF6677889900",
  "serie":       "CP",
  "folio":       "42",
  "message":     "Complemento de pago timbrado exitosamente.",
  "request_id":  "F39D0A82"
}

Confiabilidad & DX

Nuevo

Catálogos SAT para validar antes de timbrar, Idempotency-Key para prevenir doble timbrado y Webhooks para que tu sistema reciba notificaciones en tiempo real.

Validar RFC

GET

Valida el formato de un RFC mexicano antes de usarlo en una factura. Evita errores de timbrado por datos incorrectos del receptor y detecta si el RFC ya está registrado como cliente en tu negocio.

Endpoint

GET /api/validar_rfc.php?rfc={RFC}

Checks que realiza

Formato SAT (regex oficial 12/13 chars)
Tipo de persona (Física o Moral)
Fecha de nacimiento válida en el RFC
Homoclave válida (dígitos y letras)
Palabras inconvenientes SAT
RFC ya registrado como cliente en tu negocio

Ejemplo — RFC válido con cliente previo

// GET /api/validar_rfc.php?rfc=XAXX010101000
{
  "success":      true,
  "valido":       true,
  "rfc":          "XAXX010101000",
  "tipo":         "generico",
  "es_generico":  true,
  "descripcion":  "Público en General (Ventas al consumidor final)",
  "advertencias": ["RFC genérico: no emitir CFDI deducible con este receptor."],
  "request_id":   "A1B2C3"
}

// GET /api/validar_rfc.php?rfc=KOF0112111A1
{
  "success":      true,
  "valido":       true,
  "rfc":          "KOF0112111A1",
  "tipo":         "moral",
  "es_generico":  false,
  "errores":      [],
  "advertencias": [],
  "cliente_previo": {
    "cliente_id":          42,
    "nombre_razon_social": "COCA COLA FEMSA SAB DE CV",
    "email":               "facturacion@femsa.com.mx",
    "codigo_postal":       "06700",
    "regimen_fiscal":      "601"
  },
  "request_id":   "B4C5D6"
}

// GET /api/validar_rfc.php?rfc=INVALIDO
{
  "success":      true,
  "valido":       false,
  "rfc":          "INVALIDO",
  "tipo":         "desconocido",
  "errores": ["RFC demasiado corto (8 caracteres). Mínimo 12 (PM) o 13 (PF)."],
  "request_id":   "C7D8E9"
}

Tip — Flujo recomendado en el POS

  1. El cliente escribe su RFC en la pantalla del POS.
  2. POS llama a GET /api/validar_rfc.php?rfc=... — respuesta instantánea.
  3. Si valido=true y cliente_previo existe → usa el cliente_id directamente en la factura.
  4. Si no existe → llama a POST /api/clientes_sync.php para registrarlo y obtener el cliente_id.
  5. Timbra con POST /api/facturar_directo.php.

Idempotency-Key — Prevención de Doble Timbrado

Si la red falla después de que el CFDI fue timbrado pero antes de que tu POS reciba la respuesta, un reintento sin protección genera dos facturas y gasta dos timbres. Con el header Idempotency-Key el sistema detecta el reintento y devuelve la respuesta original sin volver a timbrar.

Cómo funciona

  1. Tu POS genera un UUID único por operación y lo envía en el header Idempotency-Key.
  2. Si es la primera vez que se ve esa key → se procesa normalmente y se cachea la respuesta por 24 horas.
  3. Si el POS reintenta con la misma key → se devuelve la respuesta cacheada con el header Idempotency-Replay: true, sin timbrar de nuevo.
  4. Si la primera llamada falló (4xx/5xx) → no se cachea, el reintento se procesa normalmente.

Aplica en

POST /api/facturar_directo.php

Ejemplo

# Primera llamada
curl -X POST https://tudominio.com/facturas/api/facturar_directo.php \
  -H "Authorization: Bearer {API_KEY}" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{...}'

# Reintento con misma key → misma respuesta, sin timbrar de nuevo
# Response header incluirá: Idempotency-Replay: true

Webhooks Salientes

En lugar de hacer polling a sat_status, configura una URL en tu sistema y FacturaMX te notificará automáticamente cuando ocurran eventos. Configurable desde el dashboard en Autofactura → Webhooks.

cfdi.timbrado

CFDI timbrado exitosamente vía API

cfdi.cancelado

CFDI cancelado ante el SAT

ticket.registrado

Ticket registrado por POS/ERP

ticket.facturado

Ticket marcado como facturado

Payload que recibe tu endpoint

{
  "evento":      "cfdi.timbrado",
  "negocio_id":  5,
  "timestamp":   "2025-06-04T14:30:00+00:00",
  "data": {
    "uuid":        "CE890A12-C84F-45F2-81D2-90A091FB7D42",
    "request_id": "F39D0A82",
    "ticket":      "TX-0001"   // solo si se envió en la llamada original
  }
}

Verificar la firma (recomendado)

// PHP — verifica que el request viene de FacturaMX
$body     = file_get_contents('php://input');
$recibida = $_SERVER['HTTP_X_FACTURAMX_SIGNATURE'] ?? '';
$esperada = 'sha256=' . hash_hmac('sha256', $body, $tu_secreto);
if (!hash_equals($esperada, $recibida)) {
    http_response_code(401); exit;
}
$evento = json_decode($body, true);

Headers enviados en cada request

Content-Typeapplication/json
X-FacturaMX-Signaturesha256={hmac_sha256_del_body} — usa tu secreto para verificar
X-FacturaMX-TimestampUnix timestamp del envío
X-FacturaMX-EventNombre del evento (ej: cfdi.timbrado)
User-AgentFacturaMX-Webhooks/1.0
Tu endpoint debe responder HTTP 2xx en menos de 4 segundos. Si tarda más o responde con error, el sistema reintenta hasta 5 veces con backoff (5 min, 15 min, 1h, 6h, 24h).

Ejemplo — cURL

curl -X POST https://tudominio.com/facturas/api/autofactura_webhook.php \
  -H "Authorization: Bearer afk_tu_api_key_aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "ticket": "TX-0001",
    "fecha": "2026-03-21",
    "conceptos": [{
      "descripcion": "Cafe Americano",
      "clave_sat": "90111800",
      "clave_unit": "H87",
      "cantidad": 2,
      "precio_unitario": 45.00,
      "iva_tasa": 0.16
    }]
  }'

Ejemplo — PHP

<?php
$url     = 'https://tudominio.com/facturas/api/autofactura_webhook.php';
$api_key = 'afk_tu_api_key_aqui';

$payload = [
    'ticket'    => 'TX-0001',
    'fecha'     => '2026-03-21',
    'conceptos' => [[
        'descripcion'     => 'Café Americano',
        'clave_sat'       => '90111800',
        'clave_unit'      => 'H87',
        'cantidad'        => 2,
        'precio_unitario' => 45.00,
        'iva_tasa'        => 0.16,
    ]],
];

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => json_encode($payload),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $api_key,
        'Content-Type: application/json',
    ],
]);

$res  = curl_exec($ch);
$data = json_decode($res, true);
curl_close($ch);

if ($data['success']) {
    echo 'Portal: ' . $data['portal_url'];
} else {
    error_log('[facturamx] ' . $data['code'] . ' — ' . $data['error']);
}

Ejemplo — JavaScript (Node.js)

Llama a la API siempre desde tu servidor, nunca desde el navegador del cliente.

// Node.js — fetch nativo (v18+)
const API_KEY = 'afk_tu_api_key_aqui';
const URL     = 'https://tudominio.com/facturas/api/autofactura_webhook.php';

const res  = await fetch(URL, {
  method:  'POST',
  headers: {
    'Authorization': `Bearer ${API_KEY}`,
    'Content-Type':  'application/json',
  },
  body: JSON.stringify({
    ticket: 'TX-0001',
    fecha:  '2026-03-21',
    conceptos: [{
      descripcion:     'Café Americano',
      clave_sat:       '90111800',
      clave_unit:      'H87',
      cantidad:        2,
      precio_unitario: 45.00,
      iva_tasa:        0.16,
    }],
  }),
});

const data = await res.json();

if (data.success) {
  console.log('Portal:', data.portal_url);
} else {
  console.error(`[${data.code}]`, data.error);
}

Ejemplo — Python

import requests

API_KEY = "afk_tu_api_key_aqui"
URL     = "https://tudominio.com/facturas/api/autofactura_webhook.php"

payload = {
    "ticket": "TX-0001",
    "fecha":  "2026-03-21",
    "conceptos": [{
        "descripcion":     "Café Americano",
        "clave_sat":       "90111800",
        "clave_unit":      "H87",
        "cantidad":        2,
        "precio_unitario": 45.00,
        "iva_tasa":        0.16,
    }],
}

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type":  "application/json",
}

r    = requests.post(URL, json=payload, headers=headers, timeout=10)
data = r.json()

if data["success"]:
    # Enviar data["portal_url"] al cliente
    print("Portal:", data["portal_url"])
else:
    print(f"[{data['code']}]", data["error"])

¿Listo para integrar?

Crea tu cuenta, activa el módulo de autofactura y obtén tu API Key en minutos.