POST
/v1/invoices
Crear una factura
Crea y timbra un CFDI 4.0 en una sola llamada. La respuesta llega cuando el PAC devuelve el UUID: la llamada es síncrona, así que cuando te contestamos el comprobante ya está presentado o ya sabemos por qué no.
Manda siempre
Idempotency-Key. Si repites la llamada con la misma clave te devolvemos la factura original en vez de timbrar dos veces, con la cabecera Idempotency-Replayed: true.
Parámetros
- customer objeto · requerido si no mandas customer_id
- El receptor. Lleva tax_id, legal_name, tax_regime y postal_code. Si el RFC ya está en tus clientes usamos ese registro tal cual y no lo modificamos.
- customer_id entero · requerido si no mandas customer
- Un cliente que ya tienes guardado, por su id.
- issuer_id entero · requerido si tienes más de un emisor
- Con qué RFC se timbra. Si tu cuenta tiene un solo emisor lo puedes omitir; con dos o más lo pedimos, porque adivinarlo sería timbrar con el RFC equivocado.
- cfdi_use string · opcional
- Clave del catálogo de uso del CFDI, por ejemplo G03 para gastos en general. Si lo omites usamos el uso predeterminado del cliente.
- payment_method string · opcional
- PUE cuando el pago ya se hizo en una sola exhibición; PPD cuando queda a crédito y se liquidará después con un complemento de pago. Por omisión, PUE.
- payment_form string · opcional
- Clave del catálogo de formas de pago del SAT, por ejemplo 03 para transferencia electrónica de fondos. Por omisión, 03.
- items arreglo · requerido
- Los conceptos. Cada uno lleva description, quantity y unit_price, más product_service_key y unit_key. Si mandas product_id, esos cuatro se toman del producto guardado y sólo tienes que decir la cantidad.
- items[].ieps_rate / ieps_quota decimal · opcionales
- El IEPS de la partida, a tasa o a cuota, nunca los dos: el traslado lleva un solo factor. A tasa va sobre la base; a cuota se multiplica por la cantidad, porque grava por unidad de medida y no por importe. Manda ieps_in_vat_base: false si el IEPS no debe entrar en la base del IVA; omitirlo lo deja dentro, que es lo ordinario.
- series / vat_rate string / decimal · opcionales
- La serie es A si no dices otra, y el folio siempre lo asignamos nosotros: es lo que mantiene la secuencia del emisor sin huecos. vat_rate acepta 0.16 o 0.
- tax_included booleano · opcional
- Manda true si tus precios ya traen el IVA adentro, como se ven en un menú o en una etiqueta. Nosotros separamos la base y el traslado, y el total del comprobante queda exactamente en lo que cobraste. Sin esto, bajar los precios a la base por tu cuenta desfasa el total un centavo seguido: $59 y $89 son $148 en la caja y $147.99 en la factura.
- withholding_rate decimal · opcional
- ISR retenido, 0.0125 o 0. Un emisor RESICO (régimen 626) retiene ese 1.25% cuando le factura a una persona moral. Va explícito y no lo deducimos del régimen: quién retiene depende del receptor, y esa decisión es tuya.
- vat_withholding_rate decimal · opcional
- IVA retenido, 0.106667 o 0. Son las dos terceras partes del traslado, y las retiene una persona moral que recibe honorarios o arrendamiento de una persona física. Va junto con withholding_rate y no en su lugar: el mismo recibo retiene ISR e IVA.
- currency string · opcional
- Clave del catálogo c_Moneda: MXN, USD, EUR, CAD, GBP, CHF, AUD, CNY, BRL, COP, ARS, PEN, CLP, JPY, KWD. Por omisión, MXN. Los importes de items van en esta moneda y el comprobante los escribe tal cual, sin convertirlos a pesos.
- exchange_rate decimal · condicional
- Los pesos que costaba una unidad de esa moneda el día de la emisión, hasta seis decimales. Requerido en cuanto currency no es MXN, y no se acepta cuando sí lo es: el SAT pide TipoCambio en toda moneda extranjera y lo rechaza en pesos. No lo consultamos por ti — el tipo de cambio de un comprobante es el de su fecha, y queda escrito con él.
- issued_at string · opcional
- La fecha del comprobante en ISO 8601, para facturar hoy algo de ayer. Por omisión, el instante del timbrado. El artículo 29-A la limita a las 72 horas anteriores y no admite fechar hacia adelante, así que fuera de ese rango contestamos 422 sin llamar al PAC y sin gastarte un timbre. Sin offset la leemos en la zona horaria de tu código postal de expedición, que es contra la que el SAT valida el Fecha: 2026-08-25T14:30 son las 14:30 de donde expides. Con offset explícito manda el offset.
Respuesta
Devolvemos 201 con el UUID en cuanto el PAC responde. Si el PAC rechaza el timbrado
devolvemos 422 con su motivo traducido a un código estable, y la factura en estado
error dentro de details:
el borrador se queda, para que puedas leer el rechazo contra un folio real.
- id entero
- Identificador de la factura dentro de oden.tax.
- status string
- draft, stamping, stamped, error o canceled.
- uuid string
- Folio fiscal del SAT, 36 caracteres. Llega en null si todavía no hay timbre.
- series / number string / entero
- La serie y el folio consecutivo dentro de ella.
- folio string
- Las dos anteriores juntas, que es como se lee un comprobante: A-1044.
- stamped_at string
- Momento del timbrado en ISO 8601 con zona horaria.
- subtotal / transferred_taxes / total string
- Importes con dos decimales. Van como texto a propósito: un número en JSON es un flotante. transferred_taxes es el IVA más el IEPS, igual que el TotalImpuestosTrasladados del comprobante: los dos son impuestos trasladados y el SAT los pide en un solo total.
- withheld_taxes string
- ISR retenido, ya restado del total. 0.00 en un comprobante sin retención.
- vat_withheld_taxes string
- IVA retenido, ya restado del total. Va aparte del anterior porque son dos impuestos distintos, y sumarlos deja un importe que no corresponde a ninguno.
- currency string
- Clave del catálogo c_Moneda con que se timbró el comprobante.
- exchange_rate string
- El TipoCambio del comprobante, con seis decimales. Llega en null cuando la moneda es MXN.
- issuer / customer objeto
- El emisor y el receptor tal como quedaron escritos en el comprobante.
- items arreglo
- Los conceptos con sus importes ya calculados.
- items[].transferred_taxes string
- Lo que trasladó esa partida: su IVA más su IEPS, con el mismo criterio que el campo de arriba. Las partidas suman el total del comprobante, que es lo que hace que se pueda conciliar línea por línea. Si necesitas los dos por separado, la tasa que mandaste los reconstruye; dínoslo y los devolvemos partidos.
Errores comunes
Todos tienen la misma forma: {"error": {"code", "message"}}, con
el code en inglés para que puedas hacer switch y el mensaje en español para mostrarlo.
- unauthorized
- Falta la llave o ya fue revocada. Va como Authorization: Bearer.
- plan_required
- La API está incluida en el plan Pro.
- payment_required
- La cuenta tiene un pago pendiente.
- issuer_required
- Tienes más de un emisor y la llamada no dijo cuál.
- issuer_not_ready
- Al emisor le falta su régimen, su código postal o su CSD.
- validation_failed
- Algún dato no pasó nuestras validaciones. details trae el campo y el motivo.
- csd_revoked
- Tu certificado venció o fue revocado. Súbelo de nuevo en Configuración.
- invalid_seal
- El sello no coincide con la cadena original (CFDI40102).
- duplicate_cfdi
- Ese comprobante ya tiene un timbre previo.