Empezar
Inicio rápido
De cero a tu primer CFDI timbrado por API en cinco pasos.
La API de Facty es la misma que usa la aplicación web. No hay un backend
paralelo ni un SDK obligatorio: si tu lenguaje puede mandar un POST con un
header, ya puedes timbrar.
1. Crea tu API key
Dentro de Facty: Configuración → API Keys → Nueva llave.
Le das un nombre, marcas los permisos (scopes) que la integración necesita y Facty te muestra la llave una sola vez. Guárdala en tu gestor de secretos en ese momento: se almacena hasheada con SHA-256, así que no podemos volver a enseñártela — solo revocarla.
Para timbrar facturas los scopes mínimos son:
invoices.create invoices.read
clients.read products.read
Ver Autenticación y API keys para la lista completa.
2. Prueba la conexión
Antes de emitir nada, pregunta a la API qué puedes hacer y contra qué ambiente estás hablando:
export FACTY_KEY=fk_a1b2c3...
export FACTY_ORG=mi-empresa
export FACTY_HOST=https://facty.mx
curl -s "$FACTY_HOST/api/authenticated/$FACTY_ORG/context" \
-H "X-API-Key: $FACTY_KEY" | jq
{
"org": { "id": "clx…", "slug": "mi-empresa", "name": "Mi Empresa SA de CV" },
"permissions": ["invoices.create", "invoices.read", "clients.read", "…"],
"stampingMode": "sandbox", // ← revisa esto SIEMPRE
"fiscal": {
"rfc": "MEM980101ABC",
"regimenFiscal": "601",
"lugarExpedicion": "06600",
"csd": { "state": "active", "serial": "…", "validUntil": "2027-03-14T…" },
"series": { "ingreso": "F", "egreso": "NC", "pago": "P", "nomina": null }
},
"complements": { "pagos": true, "facturaGlobal": true, "cartaPorte": false },
"timbreBalance": 1240 // solo si la llave tiene billing.read
}
Tres cosas que confirmar aquí:
stampingMode—"sandbox"o"production". Es una propiedad del ambiente, no de tu organización. Si tu integración ofrece un modo de pruebas, verifica que coincida con lo que el usuario eligió y niégate a continuar si no coincide. Equivocarse aquí significa emitir CFDIs reales, cobrables y legalmente válidos mientras el usuario cree que está probando.fiscal.csd.state— debe ser"active". Si es"missing"o"expired", el usuario tiene que subir o renovar su CSD en Facty antes de que timbres.permissions[]— lo que tu llave puede hacer. Léelo, no lo asumas.
3. Registra al cliente (receptor)
curl -s -X POST "$FACTY_HOST/api/authenticated/$FACTY_ORG/clients" \
-H "X-API-Key: $FACTY_KEY" -H "Content-Type: application/json" \
-d '{
"rfc": "XAXX010101000",
"legalName": "PUBLICO EN GENERAL",
"cp": "06600",
"regimenFiscal": "616",
"usoCfdiDefault": "S01",
"upsert": true
}' | jq
upsert: true hace que un RFC ya existente sea el caso de éxito (responde 200
con el cliente que ya estaba) en vez de un 409. Es lo que quieres en un
sincronizador que puede reintentarse. Ver Clientes.
4. Timbra la factura
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",
"idempotencyKey": "mi-erp:prod:factura:4471",
"items": [
{
"claveProdServ": "81111500",
"claveUnidad": "E48",
"description": "Servicios de desarrollo de software",
"quantity": 1,
"unitPrice": 10000,
"iva": 0.16
}
]
}' | jq
{
"id": "clxi7…",
"uuid": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890",
"serie": "F",
"folio": 118,
"status": "stamped",
"total": 11600,
"moneda": "MXN",
"stampedAt": "2026-08-28T18:04:11.000Z",
"timbresRemaining": 1239
}
5. Entrega el CFDI
# URLs firmadas, válidas 5 minutos
curl -s "$FACTY_HOST/api/authenticated/$FACTY_ORG/invoices/clxi7…/pdf" -H "X-API-Key: $FACTY_KEY"
curl -s "$FACTY_HOST/api/authenticated/$FACTY_ORG/invoices/clxi7…/xml" -H "X-API-Key: $FACTY_KEY"
# o el ZIP con XML + PDF de una vez
curl -s "$FACTY_HOST/api/authenticated/$FACTY_ORG/invoices/clxi7…/cfdi" \
-H "X-API-Key: $FACTY_KEY" -o cfdi.zip
Ver Descargar el CFDI.
Siguiente paso
- Autenticación y API keys — scopes, rotación, seguridad.
- Idempotencia y reintentos — léelo antes de escribir el cliente.
- Timbrar facturas — todos los campos de un CFDI de ingreso.
- Referencia de endpoints — la tabla completa.