Después de timbrar

Descargar y enviar el CFDI

XML, PDF, el ZIP con ambos, y el envío por correo al receptor.

Todos estos endpoints piden el scope invoices.read (salvo el envío por correo, que pide invoices.send) y funcionan igual para cualquier CFDI: facturas, notas de crédito, complementos de pago y recibos de nómina. Para un REP, usa el paymentInvoiceId.

XML

GET /api/authenticated/{org}/invoices/{invoiceId}/xml

Devuelve una URL firmada, no el archivo:

{ "url": "https://…/cfdi/clxi7….xml?X-Amz-Signature=…" }

La URL vive 5 minutos. Descárgala en cuanto la recibas; no la guardes en tu base ni se la mandes a un usuario para que la abra más tarde.

url=$(curl -s "$FACTY_HOST/api/authenticated/$FACTY_ORG/invoices/clxi7…/xml" \
  -H "X-API-Key: $FACTY_KEY" | jq -r .url)
curl -s "$url" -o factura.xml

PDF

GET /api/authenticated/{org}/invoices/{invoiceId}/pdf

Misma forma: { "url": "…" }, firmada, 5 minutos.

El PDF usa la marca configurada en Facty (Configuración → Marca): logo, colores y datos de contacto de la organización emisora.

ZIP con XML + PDF

GET /api/authenticated/{org}/invoices/{invoiceId}/cfdi

Éste devuelve el archivo directamente: Content-Type: application/zip con los dos documentos dentro. Es lo que quieres para adjuntar a un correo o guardar en tu gestor documental.

curl -s "$FACTY_HOST/api/authenticated/$FACTY_ORG/invoices/clxi7…/cfdi" \
  -H "X-API-Key: $FACTY_KEY" -o cfdi.zip

Enviar por correo

POST /api/authenticated/{org}/invoices/{invoiceId}/email

Scope: invoices.send

{
  "to": "cuentas@acme.mx",
  "message": "Adjuntamos su factura de agosto. Gracias por su preferencia."
}
CampoRequeridoNotas
tonoSi lo omites, se usa el correo registrado en el cliente. Si el cliente no tiene correo y tú no mandas to: 422.
messagenoNota que se incluye en el cuerpo. Máx. 2000 caracteres.

Facty manda el XML y el PDF adjuntos, con la marca de la organización.

Códigos de respuesta

HTTPSignifica
200Listo.
404La factura no existe o es de otra organización. Devolvemos 404, no 403: la existencia de un recurso ajeno también es información privilegiada.
404 con "XML no disponible"La factura existe pero no está timbrada, o el archivo aún no se genera. Revisa status.

Guarda lo tuyo

Facty conserva tus CFDI, pero tu propio sistema debería quedarse con el XML y el UUID de cada comprobante que emitas. Un archivo local:

  • te deja reimprimir sin depender de una llamada de red,
  • es lo que le vas a entregar a tu contador o a una auditoría,
  • y es tu respaldo si algún día migras de proveedor.

Un patrón razonable justo después de timbrar:

res = post("/invoices", body=payload)                 # 201
db.save(local_id, uuid=res["uuid"], serie=res["serie"], folio=res["folio"])

xml_url = get(f"/invoices/{res['id']}/xml")["url"]
storage.put(f"cfdi/{res['uuid']}.xml", download(xml_url))