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:

typeComprobantePágina
ingresoFactura de ventaesta
egresoNota de créditoNotas de crédito
trasladoTraslado de mercancíasesta, al final
nominaRecibo de nóminaNó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

CampoRequeridoDescripción
type"ingreso"
clientIdcuid del cliente receptor.
itemsAl menos un concepto. Ver abajo.
idempotencyKey8–128 caracteres, derivada de tu registro. Ver Idempotencia.
usoCfdiClave de c_UsoCFDI compatible con el régimen del receptor.
formaPagoClave de 2 dígitos de c_FormaPago.
metodoPago"PUE" o "PPD".
serienoSobrescribe la serie configurada en Facty. 1–25 caracteres.
monedanoCódigo de 3 letras. Default "MXN".
tipoCambionoRequerido si moneda no es MXN.
exportacionnoClave de c_Exportacion (2 caracteres). Default "01" (no aplica).
informacionGlobalnoPara el CFDI global. Ver abajo.
cfdiRelacionadosnoVer "CFDI relacionados".
observacionesnoTexto libre, máx. 1000. No es un atributo del XML: se guarda con el documento y se imprime en el PDF.
numeroOrdennoTu número de orden o pedido, máx. 100. Mismo tratamiento que observaciones.
allowRetryOnFailednoPermite 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
}
CampoRequeridoDescripción
quantitySiempre. Mayor a cero.
productIdcondicionalSi lo mandas, todo lo demás se hereda del producto.
claveProdServ, claveUnidad, description, unitPricecondicionalRequeridos los cuatro si no mandas productId.
ivano0, 0.08 o 0.16. Cualquier otro valor da 422.
ivaExentonoBooleano. Excluyente con una tasa de iva distinta de cero.
iepsnoTasa decimal.
iepsCuotanoIEPS de cuota, pesos por unidad. Excluyente con ieps.
retIsrnoTasa de retención de ISR. Máximo 0.35.
retIva, retIepsnoTasas de retención.
descuentonoTasa: 0.10 es 10%.
objetoImpnoClave de c_ObjetoImp (0108). Si la omites: 02 cuando hay impuestos, 01 cuando no.
noIdentificacionnoSKU o número de parte que se imprime.
cuentaPredialnoHasta 25 dígitos. Arrendamiento de inmuebles.
numerosPedimentonoArreglo de números de pedimento aduanal.
cuentaAcTerceroRfc + Nombre + Regimen + CpnoACuentaTerceros. 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": [ /* … */ ]
}
CampoValores
periodicidad01 diario, 02 semanal, 03 quincenal, 04 mensual, 05 bimestral
meses0112 para meses; 1318 para bimestres
anio2021–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, formaPago ni metodoPago: Facty los fija por ti.
  • Sus conceptos no admiten iva, ivaExento, ieps, iepsCuota ni descuento. Mandar cualquiera de ellos da 422 en vez de descartarlos en silencio.
  • Un traslado por carretera federal necesita además el complemento Carta Porte. Consulta complements.cartaPorte en /context antes de intentarlo: si viene false, este ambiente todavía no lo emite.

Consultar lo que timbraste

GET /api/authenticated/{org}/invoices

Scope: invoices.read

FiltroDescripción
statusdraft, pending, stamped, failed, cancelled
typeingreso, egreso, pago, nomina, retencion, traslado
clientIdcuid del cliente
uuidFolio fiscal. No distingue mayúsculas.
idempotencyKeyTu llave. La vía preferente para reconciliar.
dateFrom, dateToYYYY-MM-DD o ISO-8601 con offset
limit1–100. Default 25.
cursorPaginació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}