Factura CFDI 4.0 desde tu sistema

Tu ERP, tu punto de venta o tu tienda en línea mandan la venta en JSON; Factúralo y Punto la timbra ante el SAT, guarda el XML y se la manda a tu cliente con su PDF. Los mensajes de error vienen en español, listos para enseñárselos a quien captura.

Todas las rutas en una página. También puedes bajar la referencia OpenAPI 3.1 (.yaml) para tu editor o tu herramienta para probar APIs.

Una factura, de tu sistema al SAT

  1. Tu sistema manda la ventaPOST /facturas con la llave de la empresa
  2. Se valida y se timbraResponde con el folio fiscal; si falta un dato, dice cuál
  3. Tu cliente la recibeEl PDF y el XML a su correo; el XML queda guardado
  4. Tu sistema se enteraCon el aviso factura.timbrada, firmado

Lo que puedes hacer

Una llave por empresa y una sola API para todo lo que se timbra. Cada ruta va después de https://facturaloypunto.com/api/v1.

  • FacturasTimbra facturas de ingreso con sus complementos: comercio exterior, Carta Porte, detallista, INE, notarios, colegiaturas, leyendas fiscales e impuestos locales.POST /facturas
  • Complementos de pagoRegistra lo que te pagaron y se timbra su complemento. Si no dices qué facturas abona, se eligen por el folio o por el RFC.POST /pagos
  • Factura globalJunta las ventas que nadie facturó en el periodo y las timbra a público en general.POST /facturas/global
  • CancelarAnte el SAT, con los motivos 01 a 04 (el 01, con la factura que la sustituye; el 04, sólo la global). Te avisa cuando queda cancelada, aunque tu cliente tarde días en aceptarla.POST /facturas/{id}/cancelar
  • XML y PDFEl XML timbrado, con su addenda o sin ella, y su PDF.GET /facturas/{id}/xml · /pdf
  • Reenviar por correoLa factura otra vez, con su PDF y su XML, hasta a 10 correos.POST /facturas/{id}/reenviar
  • Traslados con Carta PorteMercancía tuya que viaja por carretera, mar o aire, o que sale al extranjero con comercio exterior.POST /traslados
  • RetencionesConstancias de retenciones e información de pagos 2.0: dividendos, intereses, pagos al extranjero, plataformas digitales…POST /retenciones
  • Tickets de autofacturaciónCada venta con su código, la liga a tu portal y su QR para imprimirlo en el ticket. Lo que nadie facture entra a la global.POST /tickets
  • Leer la ConstanciaRFC, nombre, código postal y régimen del cliente, del PDF de su Constancia de Situación Fiscal o de la foto de su QR.POST /constancia
  • Catálogos del SATBusca claves de producto o servicio y de unidad, por clave o por palabras.GET /catalogos/productos-servicios · /unidades
  • Saldo de foliosLos folios que te quedan y cuándo vencen, tu plan y la vigencia de tu sello.GET /cuenta
  • Avisos firmadosTu sistema se entera cuando se timbra, se cancela, se registra un pago o un cliente se factura solo.X-FacturaloYPunto-Firma

Empieza en tres pasos

  1. Crea tu cuenta y sube tu sello

    Crea la cuenta y sube el sello digital (CSD) de la empresa en Configuración › Empresa y sellos. El sello se queda guardado y cifrado en Factúralo y Punto: tu sistema nunca lo toca.

  2. Crea la llave

    En Negocio › Integraciones › «Para programadores», en «Llaves de la API», toca «Crear llave» y elige qué puede hacer: timbrar, consultar, cancelar, registrar pagos, ver clientes o crear tickets. Es de una sola empresa (un RFC) y se muestra una sola vez: guárdala en tu servidor.

  3. Timbra tu primera factura

    Manda la venta con tu llave y una Idempotency-Key: el id de la operación en tu sistema, como el del pedido.

    POST /facturas
    curl -X POST https://facturaloypunto.com/api/v1/facturas \
      -H "Authorization: Bearer tk_tu_llave" \
      -H "Idempotency-Key: pedido-10432" \
      -H "Content-Type: application/json" \
      -d '{
        "cliente": {
          "rfc": "EKU9003173C9",
          "nombre": "ESCUELA KEMPER URGATE",
          "codigoPostal": "42501",
          "regimenFiscal": "601",
          "usoCfdi": "G03",
          "correo": "pagos@ejemplo.test"
        },
        "conceptos": [
          {
            "descripcion": "Plan Pro mensual",
            "cantidad": 1,
            "precio": "1000.00",
            "claveProdServ": "81112100",
            "claveUnidad": "E48",
            "iva": "16"
          }
        ],
        "metodoPago": "PUE",
        "formaPago": "04",
        "enviarCorreo": false
      }'

    Responde 201 con la factura timbrada:

    Respuesta 201
    {
      "id": "5f0c…",
      "serie": "A",
      "folio": "1042",
      "uuid": "70B929A9-A7AC-4CC1-9B8E-75E4CDD04EB2",
      "total": "1160.00",
      "estado": "timbrado",
      "fecha": "2026-10-24T11:32:07",
      "pdf": "https://facturaloypunto.com/api/v1/facturas/5f0c…/pdf",
      "xml": "https://facturaloypunto.com/api/v1/facturas/5f0c…/xml"
    }

    Si mandas otra vez la misma Idempotency-Key, recibes esta misma factura con 200: nunca se timbra dos veces. Si el cliente ya está en tu catálogo, manda clienteId en lugar de sus datos; un concepto puede ir con productoId.

Reglas comunes

Llave
En cada petición, Authorization: Bearer tk_…. Cada llave es de una empresa y sólo timbra con su RFC.
Idempotencia
Todo lo que timbra pide Idempotency-Key, de 8 a 120 caracteres; usa el id de la operación en tu sistema (el del pedido, el del pago de Stripe). Repetirla con los mismos datos devuelve lo mismo y nunca timbra dos veces; con otros datos responde 409. Vale por llave y por recurso: el id de un pedido sirve para su factura y para la Carta Porte de su envío. Si recibes 202 o 503, repite con la misma llave: con otra sería otra factura.
Importes como texto
Los importes van como texto decimal: "1160.00", sin comas ni signo. También se aceptan números, pero el texto evita redondeos. Lo que no se entiende se rechaza con el campo: nunca se cambia por otro valor.
Límite
120 peticiones por minuto por llave. Al pasarte responde 429 con Retry-After: los segundos que hay que esperar. No se hizo nada, así que puedes reintentar con la misma Idempotency-Key.
Errores en español
El mensaje ya viene en español para enseñárselo tal cual a tu usuario, y problemas dice qué dato corregir:
Respuesta 422
{
  "error": {
    "codigo": "DATOS_INVALIDOS",
    "mensaje": "El descuento del concepto 1 («10%») es un importe en pesos, sin signo ni «%», por ejemplo \"200.00\". Si es un porcentaje, manda el importe que resulta.",
    "problemas": [{
      "campo": "conceptos[0].descuento",
      "codigo": "DESCUENTO_INVALIDO",
      "mensaje": "El descuento del concepto 1 («10%») es un importe en pesos, …"
    }]
  }
}
Bitácora
Cada llamada queda 60 días en la bitácora de Negocio › Integraciones › «Para programadores», con su respuesta y el motivo de cualquier rechazo.
Modo de pruebas
Con el servidor en modo simulado se timbra sin llegar al SAT, con las mismas revisiones (RFC, código postal, claves del SAT); los folios de la cuenta de pruebas sí se descuentan, igual que en producción. En producción todo es real: cada factura llega al SAT y usa un folio.

Qué datos necesitas

Lo que lleva POST /facturas según el caso. Lo que no mandes toma el valor que se dice; lo que esté mal se rechaza con el campo.

Una empresa o una persona de México
En cliente: rfc (con su dígito verificador correcto), nombre tal como en su Constancia de Situación Fiscal (en mayúsculas y sin «S.A. de C.V.»), codigoPostal fiscal (como texto: "06700") y regimenFiscal (601 para una empresa; 612, 605, 626… para una persona). usoCfdi es opcional: G03 si no lo mandas. Si ya está en tu catálogo, basta clienteId.
Una persona sin RFC
rfc XAXX010101000 y su nombre. El régimen 616, el uso S01 y el código postal de tu lugar de expedición se ponen solos.
Tu factura global, con tus ventas
El cliente PUBLICO EN GENERAL e informacionGlobal con periodicidad ("01" diaria a "05" bimestral), meses y anio. Si tus ventas son tickets de Factúralo y Punto, usa POST /facturas/global.
Un cliente del extranjero
extranjero: true, nombre, pais (USA, CAN…) y registroFiscal (su número de identificación fiscal). El RFC genérico, el régimen, el uso y el código postal se ponen solos.
De contado (PUE)
Manda siempre formaPago como texto de dos dígitos: "01" efectivo, "03" transferencia, "04" tarjeta de crédito, "28" de débito. Si falta, sale con 03 y la respuesta trae un aviso.
A crédito (PPD)
metodoPago "PPD": la forma de pago es 99 sola. Cada pago que recibas va con POST /pagos (fecha y, en facturas, el id y el abono).
En dólares
moneda "USD" y tipoCambio (pesos por dólar).
Con retenciones
Honorarios, arrendamiento y RESICO sólo los retiene una empresa cuando le paga a una persona: tu empresa debe ser persona física y tu cliente, persona moral. Para otra, retencion "otra" con sus porcentajes.
Cada concepto
descripcion, precio sin impuestos (texto, sin comas) y, del catálogo del SAT, claveProdServ y claveUnidad (búscalas en GET /catalogos/…). iva: "16" si no lo mandas. Con productoId, lo que falte sale de tu catálogo. El descuento es un importe en pesos, no un porcentaje.
Para cancelar
motivo: "02" si tiene errores, "03" si la venta no se hizo, "01" con uuidSustituto (el folio fiscal de la factura que la sustituye, hecha con relacionados tipo 04) y "04" sólo para la global.

Errores

Cada error trae un codigo que no cambia y un mensaje en español. El estado HTTP dice qué hacer: 422 es un dato por corregir, 401 pide volver a conectar, 403 lo arregla el dueño, y con 202, 429 o 503 se repite con la misma Idempotency-Key. La lista completa está en la referencia.

CódigoQué pasóQué mostrarle al usuario
DATOS_INVALIDOS · CLIENTE_INVALIDO422Falta un dato o no está bien; «problemas» dice cuál, con el nombre del campo que mandaste.El mensaje y cada problema junto a su campo, para que lo corrija y lo mande otra vez.
PAC_RECHAZO422El SAT no aceptó un dato: por ejemplo, el nombre o el código postal no son los de la Constancia del cliente.El mensaje tal cual: ya dice qué cambiar.
CSD_NO_DISPONIBLE · FOLIOS_INSUFICIENTES422Falta el sello digital (o venció) o se acabaron los folios.Que el dueño lo arregle en Factúralo y Punto; luego repite con la misma Idempotency-Key.
IDEMPOTENCIA409Esa Idempotency-Key ya se usó con otros datos.Nada: es de tu sistema. Si es otra venta, usa otra llave.
EN_PROCESO202La misma operación se está timbrando en otra petición.«Se está timbrando»; repite con la misma llave en unos segundos.
TIMBRADO_SIN_CONFIRMAR · PAC_ERROR503El servicio de timbrado no contestó y no se sabe si timbró.«Lo estamos confirmando»; repite con la MISMA llave después de Retry-After, nunca con otra.
LLAVE_REVOCADA · LLAVE_VENCIDA401La conexión ya no vale.«Vuelve a conectar tu cuenta de Factúralo y Punto».
SIN_PERMISO · FUERA_DE_PLAN · CUENTA_SUSPENDIDA403La llave no puede hacerlo, el plan no lo incluye o la cuenta está suspendida.El mensaje tal cual: lo arregla el dueño.
NO_EXISTE404No existe en esa empresa.«No encontramos esa factura».
CANCELACION_CON_RELACIONADOS409La factura tiene complementos de pago o notas de crédito vigentes.Cuáles cancelar primero: vienen en «relacionados», con su id.
CANCELACION_NO_APLICADA422El SAT no aplicó la cancelación.El mensaje: la factura sigue vigente.
DEMASIADAS_PETICIONES429Más peticiones por minuto de las que permite la llave.Nada: espera Retry-After y repite.
ERROR_INTERNO500Una falla nuestra.«Algo falló, inténtalo más tarde»; repite con la misma llave.

Avisos firmados

Agrega la dirección de tu sistema en Negocio › Integraciones › «Para programadores» y elige qué quieres saber. Por cada evento llega un POST con { id, evento, empresaId, creado, datos }, y el evento también en el encabezado X-FacturaloYPunto-Evento.

factura.timbrada
Se timbró una factura.
factura.cancelada
Se canceló una factura: en ese momento o cuando tu cliente aceptó la cancelación.
factura.cancelacion_rechazada
Tu cliente rechazó la cancelación (o no procedió): la factura sigue vigente.
pago.registrado
Se registró un pago y se timbró su complemento.
ticket.facturado
Un cliente facturó su ticket en tu portal.
folios.por_acabarse
Tu saldo de folios bajó del mínimo que pusiste.
sello.por_vencer
A tu sello digital le quedan 30, 7 o 1 días, o venció hoy.

Cómo saber que el aviso es nuestro

El encabezado X-FacturaloYPunto-Firma trae t=<segundos>,v1=<firma>, donde v1 es el HMAC-SHA256 de t.cuerpo con tu secreto, en hexadecimal. Revisa la firma y que t no tenga más de 5 minutos:

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

// cuerpo: el texto tal como llegó, antes de JSON.parse.
// firma: el encabezado X-FacturaloYPunto-Firma («t=…,v1=…»).
// secreto: el que te dimos al agregar tu dirección.
export function avisoValido(cuerpo, firma, secreto) {
  const { t, v1 } = Object.fromEntries(firma.split(",").map((parte) => parte.split("=")));
  // Sin firma o de hace más de 5 minutos: se descarta.
  if (!t || !v1 || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const esperada = createHmac("sha256", secreto).update(`${t}.${cuerpo}`).digest("hex");
  return v1.length === esperada.length && timingSafeEqual(Buffer.from(v1), Buffer.from(esperada));
}

Responde con un 2xx en menos de 8 segundos; si no, se reintenta hasta cinco veces, cada vez más espaciado (la última, 12 horas después). El id es el mismo en los reintentos: úsalo para no procesar dos veces el mismo aviso. Cada cambio se avisa una sola vez, también una cancelación que se resuelve días después. Las redirecciones no se siguen y la dirección debe ser pública y con https; para probar en tu computadora usa un túnel (cloudflared o ngrok).

Conecta a tus clientes

Para plataformas que atienden a muchos negocios: un ERP, un punto de venta, una plataforma de marketing. Cada negocio conecta su cuenta con un clic desde tu sistema y tú recibes la llave de su empresa, sin que nadie copie nada:

  1. Tu sistema manda al negocio a https://facturaloypunto.com/conectar con tu clave de plataforma (socio), un estado al azar y una de tus direcciones de regreso (regreso); si quieres, también su correo y tu id del negocio.
  2. Ahí entra o crea su cuenta, elige su empresa, sube su sello (tu sistema nunca lo ve) y te autoriza.
  3. Regresa a tu dirección con tu mismo estado y un codigo que sirve una sola vez y dura 60 segundos (o con error=cancelado).
  4. Tu servidor cambia el código por la llave de su empresa en POST /socios/canjear, con tu secreto, y si quieres deja ahí la dirección para sus avisos.
  5. Con POST /sesiones le abres Factúralo y Punto ya con su sesión: una liga de un solo uso que dura 60 segundos. Si en ese navegador no estaba su sesión, confirma con un botón; esa sesión sólo sirve para su empresa y por unas horas.
  6. POST /socios/desconectar revoca la llave, sus avisos y las sesiones que abriste. El negocio también te puede desconectar desde Negocio › Integraciones.

Cómo registrar tu plataforma

Crea tu cuenta de Factúralo y Punto y en Negocio › Integraciones › «Para programadores» › «Soy una plataforma» da de alta tu clave, tu nombre y tus direcciones de regreso (https; para probar, también http://localhost): te damos tu secreto una sola vez. Con eso ya conectas negocios y usas sus llaves. Para cancelar facturas por ellos y abrirles Factúralo y Punto con su sesión, verificamos tu plataforma: escríbenos a soporte@facturaloypunto.com.

Cada negocio le paga sus folios directo a Factúralo y Punto, a los mismos precios de abajo: tú no cobras comisión ni pagas por ellos. La conexión manual sigue sirviendo: el negocio crea su llave y su dirección de avisos en Negocio › Integraciones › «Para programadores» y las pega en tu sistema.

Precios