Saltar al contenido
oden.tax docs
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.