Empezar
Nueve pasos entre abrir la cuenta y ver su primera factura sellada en el ambiente de pruebas. Los cinco primeros ocurren en app.factadte.com; los cuatro últimos, en su editor.
Antes de escribir código
La API no crea empresas ni certificados: emite con los de una empresa que ya existe en Facta y que ya hizo su incorporación con el Ministerio. Si todavía no llegó ahí, el asistente de la app lo acompaña paso a paso, y lo de abajo lo espera al final de ese camino.
Los nueve pasos
-
Cuenta y empresa en app.factadte.com
Cree la cuenta, registre la empresa con su NIT y su NRC, y complete el perfil fiscal. De ahí salen el emisor, el establecimiento y el punto de venta que la API escribe en cada documento: usted no los nombra nunca en una petición, y por eso no los puede equivocar.
-
Certificado y vault de firma
Suba el certificado de firma que le entregó el Ministerio y guarde su contraseña en el vault de la empresa. Ese certificado nunca sale de ahí y nunca viaja a su servidor: cuando la API firma, lo abre en el momento y lo vuelve a cerrar.
-
Acuñe la llave y elija sus alcances
En Configuración → Llaves de la API. Al acuñarla decide tres cosas que después no se editan:
- Los alcances.
issuepara las rutas que emiten,querypara consultar,downloadpara el área de retención. Dé solo los que el sistema va a usar: una llave de un punto de venta no necesita descargar nada. - Los tipos de DTE que esa llave puede emitir.
- La lista de direcciones IP, si quiere una. Acepta direcciones sueltas y rangos en notación CIDR, IPv4 o IPv6. Vacía significa «desde cualquier parte».
- Los alcances.
-
Las tres cadenas, y dónde guarda cada una
Al acuñar la llave, Facta le entrega tres cadenas y las muestra una sola vez. No son tres formas de decir lo mismo: cada una abre algo distinto, y esa separación es lo que hace que perder una no sea perderlo todo.
Cadena Qué abre ¿Viaja a Facta? FACTA_API_KEYNada. Identifica y autentica. Sí, en cada petición. FACTA_SIGN_KEY(factask_…)Su vault de firma, en el servidor de Facta, durante una petición. Sí, solo al emitir, firmar y anular. FACTA_UNLOCK_KEY(factauk_…)Sus credenciales de almacenamiento, en su servidor. Nunca. sh.env de su despliegue # Las tres cadenas se muestran UNA sola vez, al acuñar la llave. FACTA_API_KEY=facta_test_k7f3a9c21.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx FACTA_SIGN_KEY=factask_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # Esta tercera NO se le pasa a Facta ni al cliente: abre sus credenciales # de almacenamiento en SU servidor. FACTA_UNLOCK_KEY=factauk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -
El ambiente lo decide la llave
Una llave
facta_test_transmite aapitest.dtes.mh.gob.sv; unafacta_live_, al servicio de producción. Las dos usan la misma URL base.Una llave de producción solo se puede acuñar para una empresa que ya pasó su propia ceremonia de paso a producción, y el servidor compara los dos ambientes antes de firmar nada. Cruzarlos es
403 environment_not_allowed: una llave de pruebas no emite un documento real, y una de producción no ensucia el ambiente de pruebas. -
Conecte un destino de almacenamiento
Es un requisito para emitir, no un extra. La empresa necesita al menos un destino conectado y verificado en los últimos 30 días —Drive, S3, R2, Supabase Storage, FTP/SFTP— o la petición se detiene con
422 no_storage_destinationantes de reservar correlativo.La razón: firmar un documento que no tiene dónde aterrizar lo dejaría sin ninguna copia salvo la de esa misma respuesta HTTP, y esa respuesta se pierde con el primer proceso que se cae.
-
La primera petición
Antes de emitir nada, confirme que la llave sirve y mire qué dice de sí misma: el ambiente, los alcances, si puede firmar y cuánto cupo le queda.
curlTerminal curl https://hcnvknpsbadplnfcflxx.supabase.co/functions/v1/api-v1/v1/status \ -H "X-Facta-Key: $FACTA_API_KEY"Si contesta
401 invalid_api_key, casi siempre falta el punto: la llave esfacta_test_<id>.<secreto>entera, y la parte de después del punto se mostró una vez. Si contesta403 ip_not_allowed, la llave tiene lista de direcciones y la suya no está en ninguna entrada. -
La primera factura de pruebas
Una factura (01) a consumidor final: sin receptor, que es legal, y con el IVA incluido en el precio de la línea —así lo define el esquema oficial de ese tipo—.
curlTerminal curl -X POST https://hcnvknpsbadplnfcflxx.supabase.co/functions/v1/api-v1/v1/dte \ -H "X-Facta-Key: $FACTA_API_KEY" \ -H "X-Facta-Sign-Key: $FACTA_SIGN_KEY" \ -H "Idempotency-Key: mi-primera-venta-001" \ -H "Content-Type: application/json" \ -d '{ "tipoDte": "01", "items": [ { "descripcion": "Café", "cantidad": 2, "precioUni": 1.5 } ] }'Un
200traeselloRecibido: eso es un documento sellado. Un202significa que quedó firmado y el Ministerio no contestó — no lo reintente, que firmaría un segundo documento. Un422conmh_rejectedsignifica que Hacienda lo leyó y lo negó. -
Antes de pasar a producción
La lista corta, y ninguna de las cuatro es opcional:
- Guardar el
jwsde cada emisión, no eldocumento. Volver a serializar el JSON no reproduce los bytes cuya firma validó Hacienda. - Manejar el
202como un resultado y no como un fallo. - Usar una
Idempotency-Keyestable por venta —su número de pedido, su ticket de caja— y no una aleatoria por intento. - Tener un camino para el
422 mh_rejected: el correlativo ya se gastó, y el §167 de la normativa permite corregir con ese mismo número.
- Guardar el
De pruebas a producción
Cuando la empresa complete su ceremonia de paso a producción en la app, acuñe una llave nueva con prefijo facta_live_. No hay nada más que cambiar: ni la URL, ni las cabeceras, ni el código.
Las llaves de pruebas siguen sirviendo y conviene conservarlas: es donde se prueba el cambio siguiente.