Emitir CFDI
Timbrar facturas
El CFDI de ingreso completo: conceptos, impuestos, moneda, factura global, relacionados y traslados.
POST /api/authenticated/{org}/invoices
Scope: invoices.create. Devuelve 201 con el CFDI ya timbrado.
Un solo endpoint emite todos los tipos de comprobante. El campo type decide qué
más se espera en el cuerpo:
type | Comprobante | Página |
|---|---|---|
ingreso | Factura de venta | esta |
egreso | Nota de crédito | Notas de crédito |
traslado | Traslado de mercancías | esta, al final |
nomina | Recibo de nómina | Nómina |
Antes de la primera llamada
Confirma con GET /context que fiscal.csd.state sea "active" y que
stampingMode sea el ambiente que crees. Sin CSD válido la respuesta es 412 y
no hay nada que tu código pueda hacer al respecto: el usuario tiene que subirlo en
Facty.
CFDI de ingreso
Campos del comprobante
| Campo | Requerido | Descripción |
|---|---|---|
type | sí | "ingreso" |
clientId | sí | cuid del cliente receptor. |
items | sí | Al menos un concepto. Ver abajo. |
idempotencyKey | sí | 8–128 caracteres, derivada de tu registro. Ver Idempotencia. |
usoCfdi | sí | Clave de c_UsoCFDI compatible con el régimen del receptor. |
formaPago | sí | Clave de 2 dígitos de c_FormaPago. |
metodoPago | sí | "PUE" o "PPD". |
serie | no | Sobrescribe la serie configurada en Facty. 1–25 caracteres. |
moneda | no | Código de 3 letras. Default "MXN". |
tipoCambio | no | Requerido si moneda no es MXN. |
exportacion | no | Clave de c_Exportacion (2 caracteres). Default "01" (no aplica). |
informacionGlobal | no | Para el CFDI global. Ver abajo. |
cfdiRelacionados | no | Ver "CFDI relacionados". |
observaciones | no | Texto libre, máx. 1000. No es un atributo del XML: se guarda con el documento y se imprime en el PDF. |
numeroOrden | no | Tu número de orden o pedido, máx. 100. Mismo tratamiento que observaciones. |
allowRetryOnFailed | no | Permite reusar una llave de idempotencia que ya falló. |
Lo que no mandas, porque sale de tu perfil fiscal en Facty: el emisor, su
régimen, el lugar de expedición, la serie y el folio (salvo que sobrescribas
serie), y por supuesto el sello y el timbre.
Conceptos (items)
Cada concepto es o bien una referencia a tu catálogo, o bien un concepto completo:
{ "productId": "clxp01…", "quantity": 2 }
{
"claveProdServ": "81111500",
"claveUnidad": "E48",
"description": "Servicios de desarrollo de software",
"quantity": 1,
"unitPrice": 10000,
"iva": 0.16
}
| Campo | Requerido | Descripción |
|---|---|---|
quantity | sí | Siempre. Mayor a cero. |
productId | condicional | Si lo mandas, todo lo demás se hereda del producto. |
claveProdServ, claveUnidad, description, unitPrice | condicional | Requeridos los cuatro si no mandas productId. |
iva | no | 0, 0.08 o 0.16. Cualquier otro valor da 422. |
ivaExento | no | Booleano. Excluyente con una tasa de iva distinta de cero. |
ieps | no | Tasa decimal. |
iepsCuota | no | IEPS de cuota, pesos por unidad. Excluyente con ieps. |
retIsr | no | Tasa de retención de ISR. Máximo 0.35. |
retIva, retIeps | no | Tasas de retención. |
descuento | no | Tasa: 0.10 es 10%. |
objetoImp | no | Clave de c_ObjetoImp (01–08). Si la omites: 02 cuando hay impuestos, 01 cuando no. |
noIdentificacion | no | SKU o número de parte que se imprime. |
cuentaPredial | no | Hasta 25 dígitos. Arrendamiento de inmuebles. |
numerosPedimento | no | Arreglo de números de pedimento aduanal. |
cuentaAcTerceroRfc + Nombre + Regimen + Cp | no | ACuentaTerceros. Los cuatro o ninguno. |
Cualquier campo que mandes explícitamente gana sobre el default del producto.
Ejemplo completo
curl -s -X POST "$FACTY_HOST/api/authenticated/$FACTY_ORG/invoices" \
-H "X-API-Key: $FACTY_KEY" -H "Content-Type: application/json" \
-d '{
"type": "ingreso",
"clientId": "clx9aa…",
"usoCfdi": "G03",
"formaPago": "03",
"metodoPago": "PUE",
"moneda": "MXN",
"numeroOrden": "OC-2026-0442",
"idempotencyKey": "mi-erp:prod:factura:4471",
"items": [
{
"claveProdServ": "81111500",
"claveUnidad": "E48",
"description": "Desarrollo de software a la medida — agosto 2026",
"quantity": 1,
"unitPrice": 48000,
"iva": 0.16,
"retIsr": 0.10,
"retIva": 0.106667
},
{ "productId": "clxp02…", "quantity": 5, "descuento": 0.15 }
]
}' | jq
Respuesta
{
"id": "clxi7…",
"uuid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890",
"serie": "F",
"folio": 118,
"status": "stamped",
"total": 51840,
"moneda": "MXN",
"stampedAt": "2026-08-28T18:04:11.000Z",
"timbresRemaining": 1239
}
Trae serie, folio y UUID en la misma respuesta justamente para que no tengas
que encadenar un GET y escribir tu registro local a medias.
timbresRemaining es el saldo tras este timbrado; puede venir null si la
lectura del saldo falló — eso nunca convierte en error una petición cuyo CFDI ya
se emitió.
Moneda extranjera
{
"type": "ingreso",
"moneda": "USD",
"tipoCambio": 18.42,
"…": "…"
}
tipoCambio es obligatorio cuando la moneda no es MXN: es el valor de una
unidad de esa moneda en pesos. Los unitPrice van en la moneda del comprobante,
no en pesos.
Factura global (público en general)
Para consolidar ventas al público en un solo CFDI, manda informacionGlobal con
un receptor genérico (XAXX010101000, régimen 616, uso S01):
{
"type": "ingreso",
"clientId": "clxpublico…",
"usoCfdi": "S01",
"formaPago": "01",
"metodoPago": "PUE",
"informacionGlobal": { "periodicidad": "04", "meses": "08", "anio": 2026 },
"idempotencyKey": "pos:prod:global:2026-08",
"items": [ /* … */ ]
}
| Campo | Valores |
|---|---|
periodicidad | 01 diario, 02 semanal, 03 quincenal, 04 mensual, 05 bimestral |
meses | 01–12 para meses; 13–18 para bimestres |
anio | 2021–2099 |
CFDI relacionados
Para vincular este comprobante con otros ya emitidos:
{
"cfdiRelacionados": [
{
"tipoRelacion": "04",
"uuids": ["A1B2C3D4-…", "E5F6A7B8-…"]
}
]
}
tipoRelacion es una clave de 2 dígitos de c_TipoRelacion (01 nota de crédito,
04 sustitución de CFDI previos, 07 aplicación de anticipo…). En una nota de
crédito este nodo es obligatorio.
CFDI de traslado
Un traslado (type: "traslado") ampara el movimiento de mercancía propia; no
tiene efecto de venta.
{
"type": "traslado",
"clientId": "clx9aa…",
"idempotencyKey": "wms:prod:embarque:9931",
"items": [
{
"claveProdServ": "43211500",
"claveUnidad": "H87",
"description": "Equipo de cómputo",
"quantity": 12,
"unitPrice": 0
}
]
}
- No lleva
usoCfdi,formaPagonimetodoPago: Facty los fija por ti. - Sus conceptos no admiten
iva,ivaExento,ieps,iepsCuotanidescuento. Mandar cualquiera de ellos da422en vez de descartarlos en silencio. - Un traslado por carretera federal necesita además el complemento Carta Porte.
Consulta
complements.cartaPorteen/contextantes de intentarlo: si vienefalse, este ambiente todavía no lo emite.
Consultar lo que timbraste
GET /api/authenticated/{org}/invoices
Scope: invoices.read
| Filtro | Descripción |
|---|---|
status | draft, pending, stamped, failed, cancelled |
type | ingreso, egreso, pago, nomina, retencion, traslado |
clientId | cuid del cliente |
uuid | Folio fiscal. No distingue mayúsculas. |
idempotencyKey | Tu llave. La vía preferente para reconciliar. |
dateFrom, dateTo | YYYY-MM-DD o ISO-8601 con offset |
limit | 1–100. Default 25. |
cursor | Paginación |
{
"invoices": [
{
"id": "clxi7…",
"serie": "F",
"folio": 118,
"type": "ingreso",
"status": "stamped",
"uuid": "A1B2C3D4-…",
"idempotencyKey": "mi-erp:prod:factura:4471",
"receptorRfc": "ACM980101ABC",
"receptorName": "ACME SA DE CV",
"total": 51840,
"moneda": "MXN",
"createdAt": "2026-08-28T18:04:09.000Z",
"stampedAt": "2026-08-28T18:04:11.000Z"
}
],
"nextCursor": null
}
El detalle completo, con conceptos e impuestos desglosados:
GET /api/authenticated/{org}/invoices/{invoiceId}