Inicio rápido
En menos de 5 minutos puedes enviar tu primer ticket desde cualquier sistema.
Obtén tu API Key
En tu dashboard → Autofactura → tab API Externa → Generar API Key.
Envía el ticket
Haz un POST a /api/autofactura_webhook.php con los datos de la venta.
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)
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
POSTRegistra 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
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
POSTEndpoint
POST /api/autofactura_ticket_cancel.php
{
"ticket": "TX-0001",
"motivo": "Devolucion total o cancelacion en POS"
}
Consultar ticket
GET / POSTEndpoint
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 | Sí | Código único de la venta. Máx. 100 chars. Ej: TX-0001 |
| fecha | string | Sí | Fecha de la venta en formato YYYY-MM-DD. Debe estar dentro del periodo permitido para el negocio. |
| conceptos | array | Sí | 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 | Sí | — | Descripción del producto o servicio. |
| clave_sat | string | Sí | — | Clave SAT de producto o servicio (8 dígitos). Ej: 81111508. Acepta también clave_prod_serv. |
| clave_unit | string | Sí | — | 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 | Sí | — | Cantidad a facturar (permite decimales). |
| precio_unitario | number | Sí | — | 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 |
|---|---|
| success | true siempre en respuesta 200. |
| ticket_id | ID interno del ticket en FacturaMX. Guárdalo para referencias futuras. |
| mode | Indica si el ticket fue created, updated o reactivated. |
| portal_url | URL 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_id | ID ú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"
}
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
Timbra CFDIs al instante, descarga XML/PDF, cancela comprobantes y mantén tus clientes sincronizados directamente desde tu backend con Bearer Token.
Facturacion Directa
POSTGenera 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 Sí son obligatorios por el SAT.
| Campo | Tipo | Req. | Default | Descripción |
|---|---|---|---|---|
| descripcion | string | Sí | — | Descripción del producto o servicio. Si va vacía, el concepto se ignora. |
| clave_sat | string | Sí | — | Clave SAT ProdServ (8 díg.). Se valida contra el catálogo. Alias: clave_prod_serv. |
| clave_unit | string | Sí | — | Clave SAT de unidad. Se valida contra el catálogo. Alias: clave_unidad. |
| precio | number | Sí | — | 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_cfdidebe ser compatible con el Régimen Fiscal del receptor. Ej:G03exige 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, usaiva_tipo="exento"o cambia aobjeto_imp="01". - Moneda distinta de
MXNrequieretipo_cambio> 0. - Método de pago
PPDobliga 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
S01y Regimen receptor616— asignados automaticamente. - Usa
"forma_pago": "99"(Por definir) cuando consolidas ventas de un periodo. - Los campos
periodicidad,mesesyanio_globalson requeridos por el SAT para el nodocfdi: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
POSTGenera 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
GETRecupera 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)
POSTSolicita 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
GETConsulta 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
POSTCrea 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.). |
| 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
GETConsulta 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)
GETConsulta 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 | Sí | 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)
POSTGenera 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_pago | string | Sí | Fecha y hora del pago. Formato YYYY-MM-DDTHH:ii:ss. Ej: "2025-06-04T14:30:00". |
| forma_pago | string | Sí | Clave SAT de la forma de pago. Ej: "03" transferencia, "01" efectivo, "04" tarjeta crédito. |
| invoices | array | Sí | Lista de CFDIs que se están pagando (mín. 1). Ver tabla de campos de invoice abajo. |
| cliente_id | int | No* | ID interno del receptor. Si se omite, se resuelve automáticamente desde invoices[0].uuid. |
| moneda | string | No | Moneda del pago. Default "MXN". |
| tipo_cambio | number | Cond. | Obligatorio si moneda ≠ MXN. |
| num_operacion | string | No | Número de referencia o folio bancario. |
| rfc_banco_ord | string | No | RFC del banco ordenante (del que sale el dinero). |
| cta_ord | string | No | Cuenta ordenante. Obligatoria si rfc_banco_ord = XEXX010101000 (banco extranjero). |
| rfc_banco_ben | string | No | RFC del banco beneficiario (al que llega el dinero). |
| cta_ben | string | No | Cuenta beneficiaria. Obligatoria si rfc_banco_ben = XEXX010101000. |
Campos de cada invoice
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| uuid | string | Sí | UUID del CFDI PPD original que se está pagando. |
| imp_saldo_ant | number | Sí | Saldo insoluto anterior (total del CFDI si es la primera parcialidad, o el saldo restante). |
| imp_pagado | number | Sí | Importe que se paga en esta ocasión (≤ imp_saldo_ant). |
| serie | string | No | Serie del CFDI original. |
| folio | string | No | Folio 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
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
GETValida 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
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
- El cliente escribe su RFC en la pantalla del POS.
- POS llama a
GET /api/validar_rfc.php?rfc=...— respuesta instantánea. - Si
valido=trueycliente_previoexiste → usa elcliente_iddirectamente en la factura. - Si no existe → llama a
POST /api/clientes_sync.phppara registrarlo y obtener elcliente_id. - Timbra con
POST /api/facturar_directo.php.
Catálogos SAT
GETConsulta los catálogos del SAT desde tu POS/ERP para validar claves antes de timbrar, eliminando errores 422 por claves inválidas.
Endpoint
GET /api/sat_catalogo.php?tipo={tipo}&q={query}
| Parámetro | Valores | Descripción |
|---|---|---|
| tipo | prod_serv | Busca en catálogo de productos/servicios SAT (tabla sat_prod_serv). Requiere q ≥ 2 chars. |
| tipo | unidad | Busca en catálogo de unidades SAT. Requiere q ≥ 2 chars. |
| tipo | uso_cfdi | Devuelve catálogo completo de Usos CFDI. Filtrable con q. |
| tipo | regimen | Catálogo de regímenes fiscales SAT. |
| tipo | forma_pago | Catálogo de formas de pago SAT. |
| tipo | metodo_pago | PUE y PPD. |
| tipo | tipo_relacion | Tipos de relación CFDI (nota crédito, sustitución, etc.). |
| q | string | Término de búsqueda. Mínimo 2 chars para prod_serv y unidad. Opcional para catálogos estáticos. |
Respuesta Exitosa (200)
{
"success": true,
"tipo": "prod_serv",
"q": "software",
"total": 3,
"data": [
{ "clave": "81111508", "descripcion": "Servicios de programacion de software" },
{ "clave": "43231513", "descripcion": "Software de contabilidad" }
],
"request_id": "A1B2C3D4"
}
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
- Tu POS genera un UUID único por operación y lo envía en el header
Idempotency-Key. - Si es la primera vez que se ve esa key → se procesa normalmente y se cachea la respuesta por 24 horas.
- Si el POS reintenta con la misma key → se devuelve la respuesta cacheada con el header
Idempotency-Replay: true, sin timbrar de nuevo. - 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-Type | application/json |
| X-FacturaMX-Signature | sha256={hmac_sha256_del_body} — usa tu secreto para verificar |
| X-FacturaMX-Timestamp | Unix timestamp del envío |
| X-FacturaMX-Event | Nombre del evento (ej: cfdi.timbrado) |
| User-Agent | FacturaMX-Webhooks/1.0 |
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.