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
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 sí 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."
}
| Campo | Requerido | Notas |
|---|---|---|
to | no | Si lo omites, se usa el correo registrado en el cliente. Si el cliente no tiene correo y tú no mandas to: 422. |
message | no | Nota 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
| HTTP | Significa |
|---|---|
200 | Listo. |
404 | La 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))