Facturación FACe (B2G)
Envía facturas FacturaE 3.2.2 a FACe — códigos DIR3, XML firmado XAdES-EPES conservado por intento, estados de tramitación, transmisiones inciertas y cómo recuperarte, anulación, simulación en sandbox y el scope facturae:write.
Facturar a una administración pública española (B2G) es obligatorio a través de FACe, el punto general de entrada de facturas electrónicas (Ley 25/2013). Factuarea genera el XML FacturaE 3.2.2 de cualquier factura emitida, lo firma XAdES-EPES con el certificado de tu empresa y lo presenta al web service de FACe — y después sigue rastreando el estado de tramitación que informa FACe hasta que la factura se paga (o se rechaza).
El ciclo de vida completo lo cubren las cinco operaciones del grupo FacturaE de la Referencia de la API:
- Descargar el XML FacturaE de una factura — firmado o sin firmar, con o sin FACe.
- Enviar una factura a FACe.
- Listar los envíos de una factura.
- Recuperar un envío para seguir su estado de tramitación.
- Solicitar la anulación de un envío.
Las lecturas usan el scope facturae:read; enviar y anular requieren
facturae:write. El módulo FacturaE está incluido desde el plan Profesional.
Todas las rutas de esta guía cuelgan de /v1/companies/{company}/…: {company} es el id (UUID v7)
del NIF con el que operas, y en los ejemplos curl es $COMPANY_ID.
Alcanzar un NIF explica cómo resolverlo.
Antes de enviar
Configura los códigos DIR3 del cliente. Todo cliente
administración pública lleva tres códigos del directorio DIR3, cada uno con
el formato ^[A-Z][A-Z0-9]{8,9}$ (p. ej. L01280796), y opcionalmente un
cuarto, el proponente. Configúralos al
crear o actualizar el cliente:
curl -X PATCH https://api.factuarea.com/v1/companies/$COMPANY_ID/contacts/0197a2a8-4cf0-7a31-9a5e-3f2b8c1d6e42/customer-profile \
-H "Idempotency-Key: $(uuidgen)" \
-H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
-H "Content-Type: application/json" \
-d '{
"dir3_accounting_office": "L01280796",
"dir3_managing_body": "L01280796",
"dir3_processing_unit": "L01280796"
}'| Campo | Rol DIR3 |
|---|---|
dir3_accounting_office | Oficina contable (01) |
dir3_managing_body | Órgano gestor (02) |
dir3_processing_unit | Unidad tramitadora (03) |
dir3_proponent | Proponente (04), opcional |
La administración te indica los códigos (los tres primeros a menudo coinciden); también puedes consultarlos en el directorio público DIR3. La operación reemplaza el perfil, así que un código que dejes fuera se borra: envía todos los que quieras conservar.
La factura captura estos códigos cuando se crea, y puedes sustituirlos por
factura o corregirlos tras emitir con una
revisión administrativa. El encaminamiento
que cuenta es el de la factura, no el del contacto en vivo; los roles 01 a 03
son obligatorios para enviar y el 04 es opcional. Si la factura tiene un
encaminamiento conocido sin centros, el envío falla con
client_missing_dir3_codes aunque el contacto tenga códigos.
Sube un certificado de firma activo. FACe solo acepta facturas
firmadas, así que el envío requiere el certificado FNMT (PKCS#12) que tu
empresa ya usa para VeriFactu
(POST /v1/companies/{company}/verifactu/certificates). Sin certificado activo
el envío falla con signing_certificate_required.
Emite la factura. Las facturas en borrador no pueden viajar a FACe —
enviar o emitir antes la factura es lo que congela su contenido legal. Los borradores y las facturas programadas (aún sin número fiscal) responden invoice_not_emittable_for_facturae.
Descargar el XML FacturaE
Puedes descargar el XML en cualquier momento — para presentación manual,
archivo o validación — sin involucrar a FACe. Cualquier factura que ya tenga
número fiscal es exportable (también una cobrada, anulada o cancelada); solo
las facturas draft y scheduled no lo son. Una factura anterior sin
calendario de pagos capturado se exporta sin el bloque de vencimientos:
Factuarea nunca lo inventa.
curl -OJ https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/0197b1c2-89ab-7def-8123-456789abcdef/facturae \
-H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW"Con un certificado activo el cuerpo va firmado XAdES-EPES (política de firma
Facturae v3.1) y el fichero se llama .xsig; sin él, el XML vuelve sin
firmar como .xml. El header de respuesta X-Facturae-Signed: true|false
distingue ambos casos. Consulta la
referencia del endpoint.
La descarga tolera la ausencia de certificado (obtienes el XML sin firmar); el envío a FACe no — FACe exige la firma.
Para obtener el fichero exacto que se presentó en un intento, añade
submission_id:
curl -OJ "https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/0197b1c2-89ab-7def-8123-456789abcdef/facturae?submission_id=0197c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d" \
-H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW"Eso devuelve los bytes conservados de ese intento: nunca regenera, vuelve a
firmar ni lee el certificado o el contacto actuales, así que es la evidencia de
lo que recibió la administración aunque la factura, los códigos DIR3 o el
certificado hayan cambiado después. La respuesta añade X-Facturae-Sha256 (y el
mismo valor como ETag) cuando se conoce el hash del fichero conservado. Un
fichero ausente o corrupto responde 500 facturae_artifact_unavailable; no se
regenera. La factura y el intento deben corresponderse; si no, 404.
Enviar a FACe
La operación de envío no necesita cuerpo de petición: la factura viaja en la ruta. Cada llamada prepara un intento: congela la factura, el encaminamiento DIR3 efectivo (proponente incluido), el calendario de pagos, el certificado que firma y el XML firmado exacto, y solo entonces habla con FACe:
curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/0197b1c2-89ab-7def-8123-456789abcdef/face-submissions \
-H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
-H "Idempotency-Key: $(uuidgen)"Respuesta (201 para un intento nuevo):
{
"data": {
"id": "0197c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"object": "face_submission",
"invoice_id": "0197b1c2-89ab-7def-8123-456789abcdef",
"status": "submitted",
"registry_number": "202612345678",
"dir3_accounting_office": "L01280796",
"dir3_managing_body": "L01280796",
"dir3_processing_unit": "L01280796",
"error_code": null,
"error_message": null,
"status_updated_at": "2026-06-12T10:15:00Z",
"last_polled_at": null,
"created_at": "2026-06-12T10:15:00Z",
"dir3_proponent": null,
"administrative_version": 1,
"administrative_revision_id": null,
"payment_terms_version": 1,
"source_fingerprint": "6d2f8a4c1b73e5b0a9c4d7e1f3a6b8c2d5e9f0a1b4c7d8e2f3a5b6c9d0e1f2a3",
"artifact_sha256": "b4c7d8e2f3a5b6c9d0e1f2a36d2f8a4c1b73e5b0a9c4d7e1f3a6b8c2d5e9f0a1",
"prepared_at": "2026-06-12T10:14:58Z",
"transmission_started_at": "2026-06-12T10:14:59Z",
"can_transmit": false,
"requires_reconciliation": false,
"signing_certificate_fingerprint": "e5b0a9c4d7e1f3a6b8c2d5e9f0a1b4c7d8e2f3a5b6c9d0e1f2a36d2f8a4c1b73",
"artifact_status": "ready"
}
}registry_number es el asiento registral de FACe que acredita la
presentación — consérvalo para cualquier disputa con la administración. Los
campos adicionales identifican lo que congeló el intento: source_fingerprint,
artifact_sha256 (hash del XML conservado), la administrative_version y la
payment_terms_version usadas y la huella del certificado. artifact_status es
ready, unverified (un intento anterior al hash), unavailable o corrupt.
El intento es la unidad, no la llamada
El cuerpo es opcional. Sin él, Factuarea crea la identidad del intento y responde
201. También puedes enviar tu propio submission_id (un UUID v7) en el cuerpo:
curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/invoices/0197b1c2-89ab-7def-8123-456789abcdef/face-submissions \
-H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "submission_id": "0197c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d" }'- Un
submission_idnuevo crea un intento nuevo:201. - Uno existente de esa factura lo continúa:
200, con el mismo XML conservado y el mismo certificado, sin volver a mirar el certificado actual ni el contacto. Si FACe ya lo había aceptado, recibes el resultado guardado y no se envía nada dos veces. submission_id: nullse rechaza, y un id de otra factura o de otra empresa es un404.- El
Idempotency-Keyno sustituye alsubmission_id: la clave reproduce una petición HTTP; el id identifica el intento duradero. Las operaciones de envío y cancelación están declaradas como no reintentables automáticamente (x-no-retry): los SDK no las reintentan, así que gestiona los fallos como se describe a continuación en lugar de envolver la llamada en un bucle de reintentos a ciegas.
Si la llamada termina con 502 face_transmission_failed (FACe caído, timeout,
respuesta mal formada) o 500 face_submission_completion_failed, el intento
existe y error.details.attempt_id te da su id. No envíes nunca un envío nuevo
a ciegas: lista los intentos de la factura
(historial)
o recupera ese, y mira su status:
status tras el fallo | Qué significa | Qué haces |
|---|---|---|
prepared | Congelado y firmado, nunca transmitido. | Reintenta con el mismo submission_id. |
transmission_uncertain | La petición salió hacia FACe y se desconoce el resultado. Un timeout no demuestra que FACe no la recibiera. | No reenvíes. Las llamadas responden 409 face_transmission_uncertain con el mismo attempt_id hasta que el intento se concilie, algo que Factuarea hace con evidencia auditada (requires_reconciliation: true); contacta con soporte. |
submitted y posteriores | FACe lo tiene. | Síguelo como cualquier envío. |
error | El intento falló. Solo se libera si FACe lo rechazó explícitamente (error_code: face_rejected) o se confirmó la no recepción (confirmed_not_received). | En esos casos corrige la causa y envía de nuevo con una identidad nueva; si no, sigue bloqueado. |
Un intento prepared o en cualquier estado de tramitación no final bloquea uno
nuevo para la misma factura (409 face_submission_already_exists), y uno incierto
responde 409 face_transmission_uncertain; rejected y cancelled lo liberan. Un
409 face_submission_attempt_conflict significa que otra petición ya tiene o ha
terminado ese intento: léelo de nuevo.
Seguir el estado de tramitación
FACe informa de cómo la administración tramita la factura. Factuarea consulta FACe periódicamente y actualiza cada envío — recuperar el detalle del envío (o el historial de envíos de la factura) es la forma de seguir el progreso; no hay endpoint de refresco en v1:
curl https://api.factuarea.com/v1/companies/$COMPANY_ID/face-submissions/0197c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d \
-H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW"status | Significado |
|---|---|
prepared | XML firmado conservado, aún sin transmitir. Continúalo con su submission_id. |
transmission_uncertain | La transmisión empezó y se desconoce el resultado. Bloquea nuevos envíos hasta que se concilie. |
submitted | Presentada en FACe; número de registro asignado. |
registered_rcf | Registrada en el RCF (el registro contable de facturas de la administración). |
accounted | Reconocida como obligación contable por la administración. |
paid | La administración informa de la factura como pagada. |
rejected | Rechazada por la administración — consulta el motivo en FACe y emite una factura corregida. |
cancellation_requested | Has solicitado la anulación; pendiente de confirmación de FACe. |
cancelled | Anulación confirmada por FACe. |
error | Error local de transmisión — error_code y error_message llevan el detalle. |
¿Prefieres push a polling? Suscríbete a los
eventos de webhook facturae.face_submitted,
facturae.face_status_changed y facturae.face_cancellation_requested.
Solicitar la anulación
Mientras la factura no se haya pagado ni rechazado puedes
solicitar su anulación
(anulación 4200). El reason es obligatorio y viaja a FACe:
curl -X POST https://api.factuarea.com/v1/companies/$COMPANY_ID/face-submissions/0197c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d/cancel \
-H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
-H "Content-Type: application/json" \
-d '{ "reason": "Factura emitida por error al organismo equivocado." }'La anulación solo se permite en un estado anulable (submitted,
registered_rcf, accounted); en caso contrario la llamada responde
face_submission_not_cancellable. El envío pasa a
cancellation_requested hasta que FACe confirma el estado final
cancelled.
Modo de prueba
Con una clave de prueba (fact_test_) todo el flujo se simula: ninguna
llamada SOAP llega a FACe y el envío recibe un número de registro sintético
con prefijo FACE-SANDBOX-*. Las validaciones de firma y DIR3 siguen
aplicando, así que el sandbox ejercita los mismos caminos de error que
producción. Consulta el modo de prueba.
Errores
| HTTP | code / subcode | Cuándo |
|---|---|---|
| 404 | resource_not_found | La factura o el envío no existe o pertenece a otra empresa. |
| 422 | business_rule_violation / invoice_not_emittable_for_facturae | La factura está en borrador o programada, así que no tiene número fiscal — emítela primero. |
| 422 | business_rule_violation / client_missing_dir3_codes | Al cliente le falta uno o más códigos DIR3. |
| 422 | business_rule_violation / signing_certificate_required | Sin certificado de firma activo — sube uno vía POST /v1/companies/{company}/verifactu/certificates. |
| 422 | business_rule_violation / face_submission_not_cancellable | El envío no está en un estado anulable. |
| 409 | resource_already_exists / face_submission_already_exists | Ya existe un envío activo para la factura. |
| 422 | face_submission_rejected | FACe analizó el envío y lo rechazó con su propio código (código DIR3 erróneo, firma no válida, duplicado); no registró la factura. details.attempt_id identifica el intento (su error_code es face_rejected): corrige los datos y envía de nuevo con una identidad nueva. |
| 409 | face_transmission_uncertain | Un intento anterior tiene un resultado desconocido. No se transmite nada; details.attempt_id es el intento que debes revisar. |
| 409 | face_submission_attempt_conflict | Otra petición tiene o ya completó ese intento. Léelo de nuevo. |
| 500 | face_submission_completion_failed | No se registró el resultado; el intento se conserva (details.attempt_id) y sigue bloqueado. |
| 500 | facturae_artifact_unavailable | El XML conservado del intento falta o está corrupto; no se regenera. |
| 403 | facturae_operation_forbidden | La autoridad vigente del emisor no permite la operación (enviar exige autoridad de envío; cancelar exige acceso de edición al módulo). |
| 403 | insufficient_scope | La clave no tiene el scope facturae:write. |
| 502 | face_transmission_failed | El web service de FACe falló o agotó el tiempo. El intento se conserva y details.attempt_id lo identifica: mira su status antes de hacer nada más. |