Recetas
Cada receta encadena en orden las operaciones de un caso de uso. El detalle de cada operación está en la guía que enlaza la receta.
En los ejemplos:
- La URL base es
https://app.faxtal.com/api/v1.1. fctXXXXXXXX_YYYYYYYYYYYYYYYYes una API key ficticia: pon la tuya.- Los identificadores, como
00000000-0000-0000-0000-000000000001, son ficticios. - Los ficheros
.jsoncontienen facturas en formato canónico.
Emitir y enviar una factura
Scopes: invoices:write, invoices:issue, invoices:read y compliance:read.
-
Guarda el borrador con
PUT /api/v1.1/drafts. No envíesheader.invoice_numberniissue_data.issue_date.curl -X PUT https://app.faxtal.com/api/v1.1/drafts \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d @factura.jsonRespuesta
201:{"success": true, "id": "00000000-0000-0000-0000-000000000001", "errors": []}. Sisuccessesfalse, el borrador se ha guardado con errores: corrígelos y reenvíalo con?invoice_id=00000000-0000-0000-0000-000000000001. -
Emite el borrador con
POST /api/v1.1/issued-invoices/{invoice_id}/issue. El sistema asigna el número y la fecha.curl -X POST https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001/issue \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"Respuesta
200: la factura constatus: "issued",numbereissue_date. Elidno cambia. Si no supera la validación,422convalidation_failedyerrors[]. -
Comprueba el registro VeriFactu, si la empresa lo tiene activo, con
GET /api/v1.1/invoices/{invoice_id}/verifactu/records. El envío a la AEAT es asíncrono: elestadoempieza enpending.curl https://app.faxtal.com/api/v1.1/invoices/00000000-0000-0000-0000-000000000001/verifactu/records \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" -
Descarga el PDF con
GET /api/v1.1/issued-invoices/{invoice_id}/pdf.curl https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001/pdf \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -o factura.pdf -
Envíala por email con
POST /api/v1.1/issued-invoices/{invoice_id}/send. Sinto, se envía al email del cliente.curl -X POST https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001/send \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d '{"to": ["cliente@example.com"]}'Respuesta
200:{"invoice_id": "00000000-0000-0000-0000-000000000001", "sent_to": ["cliente@example.com"]}. Si no hay a quién enviarla,422conno_recipient_email.
Guías: Borradores, Emitidas, Cumplimiento y Emailing.
Emitir con el número de tu programa
Scopes: invoices:write e invoices:issue.
Para cuando tu programa ya numera las facturas y quieres emitirlas en Faxtal con ese número.
-
Guarda el borrador con
PUT /api/v1.1/drafts, sinheader.invoice_numberniissue_data.issue_date, como en la receta anterior. Enheader.invoice_series_code, pon la serie cuya plantilla sigue el número de tu programa.curl -X PUT https://app.faxtal.com/api/v1.1/drafts \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d @factura.json -
Emite con tu número enviando
invoice_numberen el cuerpo dePOST /api/v1.1/issued-invoices/{invoice_id}/issue. Va completo, con la serie.curl -X POST https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001/issue \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d '{"invoice_number": "A-0042"}'Respuesta
200: la factura constatus: "issued"ynumber: "A-0042". Si el número no encaja con la plantilla de la serie,422conforced_number_invalid. Si ya lo tiene otra factura de la serie,409conforced_number_in_use. Si el borrador no supera la validación,422convalidation_failed, y el número no se usa. -
Emite en el orden de tu programa. La API no comprueba que la numeración sea correlativa: no dejes huecos ni emitas números fuera de orden.
Registrar recibidas con reconocimiento automático
Scopes: received:write e invoices:read.
-
Reconoce el documento con
POST /api/v1.1/received-invoices/recognize. Envía un PDF de hasta 8 páginas o una imagen JPEG, PNG o TIFF, de hasta 10 MB, en el campofile. No registra nada.curl -X POST https://app.faxtal.com/api/v1.1/received-invoices/recognize \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -F "file=@factura-proveedor.pdf"Respuesta
200:canonical_draft(la factura propuesta en formato canónico),score(la confianza del reconocimiento) ywarnings. Cada documento consume el cupo mensual de reconocimiento del plan. Al agotarlo,402conquota_exceeded. -
Revisa la propuesta en tu sistema. Mira
scoreywarnings, corrige lo que haga falta y guardacanonical_draftenrecibida.json. Quitaheader.is_simplifiedyheader.is_corrective: los calcula el sistema y la API los rechaza. Después, valídala sin guardar conPOST /api/v1.1/received-invoices/validate.curl -X POST https://app.faxtal.com/api/v1.1/received-invoices/validate \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d @recibida.jsonRespuesta
200:is_validyerrors[]. Un errorinferred_fields_not_allowedseñala enpathotro campo que debes quitar. -
Registra la factura con
PUT /api/v1.1/received-invoices.curl -X PUT https://app.faxtal.com/api/v1.1/received-invoices \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d @recibida.jsonRespuesta
201:{"success": true, "id": "00000000-0000-0000-0000-000000000003", "errors": []}. Si ya existe una recibida del mismo proveedor, con el mismo número y del mismo ejercicio, se actualiza. -
Adjunta el documento original con
POST /api/v1.1/received-invoices/{invoice_id}/documents.curl -X POST https://app.faxtal.com/api/v1.1/received-invoices/00000000-0000-0000-0000-000000000003/documents \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -F "file=@factura-proveedor.pdf"Respuesta
201: los datos del documento adjunto.
Guías: Recibidas, Terceros y productos y Errores.
Rectificar una factura
Scopes: invoices:read, invoices:write e invoices:issue.
Hay dos caminos. El asistido deriva la rectificativa de la original y es el recomendado: copia contraparte, líneas e impuestos, y rellena solo con lo que tú no puedes deducir. El manual te deja montar el canónico entero, por si necesitas control total.
Camino asistido
-
Pide la rectificativa con
POST /api/v1.1/issued-invoices/{invoice_id}/corrective, sobre elidde la original. Permiso de la clave:invoices:write.curl -X POST https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001/corrective \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d '{"correction_type": "D", "reason_code": "20", "series_code": "R", "external_ref": "ERP-ABONO-123", "lines": [{"description": "Descuento no aplicado", "quantity": 1, "unit_price_without_tax": -100.0}]}'Campo Descripción correction_typeS(sustitución) oD(diferencias).reason_codeMotivo, del catálogo de motivos de rectificación. series_codeSerie de la rectificativa. external_refObligatorio. Referencia de la rectificativa en tu sistema (hasta 60 caracteres). Es suya: no se hereda de la original. linesObligatorio con D: las líneas de la diferencia, con su signo.reason_descriptionTexto libre opcional. corrected_taxable_base,corrected_tax_amountSolo con S. Si no los mandas, se toman los de la original.Responde
201con eliddel borrador:{"success": true, "id": "...", "errors": []}.La original tiene que estar emitida o enviada (
409 invoice_not_issued_for_correction). Si los datos no permiten construir la rectificativa,422 corrective_draft_invalidcon el motivo enreason(por ejemplolines_required_for_differences). -
Repásala y ajústala si hace falta, con
GET /api/v1.1/drafts/{id}/canonicalpara ver qué ha quedado yPUT /api/v1.1/drafts?invoice_id={id}para cambiarlo. ConS(sustitución) la rectificativa hereda las líneas de la original: si la sustitución cambia el contenido, manda tú las líneas nuevas. -
Emítela con
POST /api/v1.1/issued-invoices/{id}/issue, igual que cualquier borrador. La ruta de rectificar no emite: expedir una factura es siempre una llamada aparte, con su permisoinvoices:issue.
Camino manual
-
Localiza la factura original con
GET /api/v1.1/issued-invoices/{invoice_id}. Anota sunumbery suissue_date.curl https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001 \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" -
Guarda la rectificativa como borrador con
PUT /api/v1.1/drafts. Enheader, usainvoice_type_code"03", o"04"si la original es simplificada, y añade el bloquecorrective:"header": { "invoice_series_code": "R", "invoice_type_code": "03", "corrective": { "invoice_number": "A-2026-0042", "invoice_issue_date": "2026-03-10", "reason_code": "20", "correction_type": "D" } }invoice_numbereinvoice_issue_date: los de la original.reason_code: el código del motivo, de"01"a"25".correction_type:D(por diferencias) oS(por sustitución). Por diferencias, las líneas y los totales recogen solo la diferencia. Por sustitución, añadecorrected_taxable_baseycorrected_tax_amount.
curl -X PUT https://app.faxtal.com/api/v1.1/drafts \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d @rectificativa.jsonRespuesta
201:{"success": true, "id": "00000000-0000-0000-0000-000000000004", "errors": []}. Si falta algún dato de la rectificación,successesfalseyerrors[]trae códigos comocorrective_reason_required. -
Emite la rectificativa con
POST /api/v1.1/issued-invoices/{invoice_id}/issue.curl -X POST https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000004/issue \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" -
Comprueba el vínculo con
GET /api/v1.1/issued-invoices/{invoice_id}. En la rectificativa,corrections.correctsidentifica la original:corrected_numbery, si está en Faxtal,corrected_invoice_id. En la original,corrections.corrected_bylista sus rectificativas.curl https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000004 \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"
Guías: Borradores, Emitidas y Formato canónico.
Migrar históricos importando
Scopes: invoices:read, invoices:issue para las emitidas y received:write para las
recibidas.
-
Prepara las facturas en formato canónico con su número (
header.invoice_number) y su fecha (issue_data.issue_date). Pon tu referencia enmeta.external_ref. -
Valida una muestra con
POST /api/v1.1/issued-invoices/validate. En un lote, una factura aceptada no informa de sus errores de validación: detéctalos antes.curl -X POST https://app.faxtal.com/api/v1.1/issued-invoices/validate \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d @historica.jsonRespuesta
200:is_validyerrors[]. -
Importa las emitidas por lotes con
POST /api/v1.1/issued-invoices/import/batch. El cuerpo lleva unbatch_idtuyo y la listainvoices:{ "batch_id": "migracion-2025-001", "invoices": [ { "schema_version": "json_1_1", "meta": { ... }, "invoice": { ... } } ] }curl -X POST https://app.faxtal.com/api/v1.1/issued-invoices/import/batch \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/json" \ -d @lote-001.jsonRespuesta
200, al terminar el lote:status(completed,completed_with_errorsofailed),total,accepted,rejectedeitems[], conindex,status,id,numberyerrorsde cada factura. Un ítem rechazado no detiene al resto. La respuesta detalla como máximo 500 ítems: usa lotes de ese tamaño o menores. -
Reenvía los rechazados después de corregirlos. Reimportar una factura con la misma serie y número actualiza la importada, salvo que ya esté bloqueada.
-
Importa las recibidas una a una: no hay lote. Con JSON canónico, usa
PUT /api/v1.1/received-invoices. Con XML, usaPOST /api/v1.1/received-invoices/importcon el formato enformat(facturae_3_2_2,facturae_3_2_1,facturae_3_2,ubl_2_1ocii_d16b).curl -X POST "https://app.faxtal.com/api/v1.1/received-invoices/import?format=ubl_2_1" \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \ -H "Content-Type: application/xml" \ --data-binary @recibida.xmlRespuesta
201:{"success": true, "id": "00000000-0000-0000-0000-000000000003", "errors": []}.
Las facturas importadas quedan como emitidas fuera de Faxtal y no generan registros VeriFactu. Para migrar desde Excel, ver importación.
Guías: Importación, Emitidas, Recibidas y Formatos.
Seguir el estado VeriFactu de un lote
Scopes: invoices:issue y compliance:read.
-
Emite cada borrador con
POST /api/v1.1/issued-invoices/{invoice_id}/issue. La API pública no emite en bloque. Guarda elidy elnumberde cada factura, y reparte las llamadas para no superar el límite de peticiones de la clave.curl -X POST https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001/issue \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" -
Consulta el registro de cada factura con
GET /api/v1.1/invoices/{invoice_id}/verifactu/records. Mientras elestadoseapending, la AEAT aún no ha respondido: vuelve a consultar más tarde, espaciando las consultas.curl https://app.faxtal.com/api/v1.1/invoices/00000000-0000-0000-0000-000000000001/verifactu/records \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" -
Revisa las incidencias con
GET /api/v1.1/verifactu/incidents. Cada incidencia traenum_serie,estado_registro,codigo_error,descripcion_errorysubmission_id. Cruzanum_seriecon elnumberde tus facturas.limitfija cuántos envíos recientes se revisan, hasta 500.curl "https://app.faxtal.com/api/v1.1/verifactu/incidents?limit=100" \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" -
Consulta los envíos, si necesitas el detalle, con
GET /api/v1.1/verifactu/submissions.curl "https://app.faxtal.com/api/v1.1/verifactu/submissions?offset=0&limit=50" \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"
Si la aplicación está emitiendo en bloque en la misma serie, emitir responde 409 con
bulk_issue_series_active. Espera a que termine.
Guías: Cumplimiento, Emitidas y Webhooks (roadmap).
Operar varias empresas
Scopes: los de cada operación. Consultar la empresa de la clave no exige ninguno.
-
Crea una API key en cada empresa. Cada clave trabaja sobre una sola empresa, que la API deduce de la clave. Ver autenticación.
-
Comprueba a qué empresa apunta cada clave con
GET /api/v1.1/company. Guarda en tu integración la relación entre eltax_idde la empresa y su clave.curl https://app.faxtal.com/api/v1.1/company \ -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"Respuesta
200:id,name,legal_name,tax_idy el resto de datos de la empresa.GET /api/v1.1/companiesdevuelve una lista con esa misma empresa. -
Usa en cada petición la clave de su empresa. Por ejemplo, un borrador de la empresa B se guarda con la clave de B:
curl -X PUT https://app.faxtal.com/api/v1.1/drafts \ -H "X-API-Key: fctBBBBBBBB_ZZZZZZZZZZZZZZZZ" \ -H "Content-Type: application/json" \ -d @factura-empresa-b.json -
Trata cada empresa por separado. Cada clave tiene su propio límite de peticiones, y cada empresa, su plan: un
402en una empresa no afecta a las demás.
Guías: Multiempresa, Autenticación y Errores.