Faxtal
Facturas

Borradores

Un borrador es una factura emitida que todavía no tiene número ni fecha. Lo guardas, lo validas y, cuando está listo, lo emites.

Todas las llamadas usan la URL base https://app.faxtal.com/api/v1.1 y la cabecera X-API-Key (ver Autenticación). El cuerpo de una factura es el formato canónico json_1_1.

OperaciónScope de la API key
Consultar, listar, validar y ver eventosinvoices:read
Crear, actualizar y eliminarinvoices:write
Emitirinvoices:issue

Sin el scope necesario, la API responde 403 con el código scope_missing.

En la API, un borrador tiene siempre el estado draft, tenga o no errores de validación.


Qué lleva un borrador

  • meta.direction: issued.
  • meta.source_system: el nombre de tu sistema.
  • meta.external_ref: tu referencia de la factura. Es obligatoria: identifica el borrador cuando lo vuelves a enviar.
  • invoice.supplier: tu empresa. Si informas supplier.tax_id, tiene que ser el NIF de la empresa de la API key.
  • invoice.header.invoice_series_code e invoice.header.invoice_type_code.
  • Ni header.invoice_number ni issue_data.issue_date: los asigna la emisión.

Una factura usa un solo grupo de impuesto: el de issue_data.tax_group_code. Si no lo envías, se usa el de tu empresa, y los tipos de las líneas (tax_output_rate) se interpretan dentro de ese grupo.

:::info Aún no disponible El grupo IGIC (Canarias) todavía no está disponible. Una factura con IGIC se rechaza con 422 y el código tax_group_not_available en errors. Hoy el grupo es siempre IVA. :::

Campos que no se envían

El sistema deduce estos campos. Si llegan en el cuerpo, la petición se rechaza con 422 inferred_fields_not_allowed y la lista de rutas en fields.

BloqueCampos
headeris_simplified, is_corrective
lines[]taxable, exempt, tax_category_code, tax_output_code, tax_withheld_code
taxes_outputs[] y taxes_withheld[]taxable, exempt, tax_category_code, tax_type_code
country_subdivision de una direcciónsubdivision_type

El tratamiento fiscal de una línea se declara con tax_treatment_code y el porcentaje (tax_output_rate).


Crear o actualizar un borrador

PUT /api/v1.1/drafts

  • Sin parámetros, crea un borrador o actualiza el que tenga la misma meta.external_ref.
  • Con ?invoice_id=<id>, actualiza ese borrador.
  • Si la factura encontrada ya no es un borrador (emitida, importada o recibida), responde 409 invoice_update_not_allowed y no la cambia.

borrador.json:

{
  "schema_version": "json_1_1",
  "meta": {
    "direction": "issued",
    "source_system": "mi-erp",
    "external_ref": "PEDIDO-2026-001"
  },
  "invoice": {
    "supplier": {
      "person_type_code": "J",
      "tax_id": "B10000008",
      "name": "Empresa Demo S.L."
    },
    "customer": {
      "person_type_code": "J",
      "tax_id": "B10000016",
      "name": "Cliente Prueba S.L.",
      "company": {
        "legal_name": "Cliente Prueba S.L.",
        "address": {
          "address": "Av. Cliente 5",
          "post_code": "08001",
          "town": "Barcelona",
          "country_code": "ES",
          "country_subdivision": { "code": "ES-B" }
        }
      }
    },
    "header": {
      "invoice_series_code": "A",
      "invoice_type_code": "01"
    },
    "issue_data": {
      "tax_currency_code": "EUR",
      "invoice_description": "Consultoría noviembre 2026"
    },
    "totals": {
      "total_gross_amount_before_taxes": 1000.0,
      "total_tax_outputs": 210.0,
      "invoice_total": 1210.0
    },
    "lines": [
      {
        "item_type": "service",
        "item_description": "Consultoría senior",
        "quantity": 10,
        "unit_of_measure": "HUR",
        "unit_price_without_tax": 100.0,
        "tax_treatment_code": "TAXABLE_NON_EXEMPT",
        "tax_output_rate": 21.0,
        "article_code": "CONSULT-SENIOR"
      }
    ]
  }
}
curl -X PUT "https://app.faxtal.com/api/v1.1/drafts" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \
  -H "Content-Type: application/json" \
  -d @borrador.json

Respuesta 201, también cuando actualiza:

{
  "success": true,
  "id": "00000000-0000-0000-0000-000000000001",
  "errors": []
}
  • id: el identificador del borrador. Lo usas en el resto de operaciones.
  • success: false si el borrador se guardó con errores de negocio. Los tienes en errors, cada uno con source, code, message, path y severity (error impide emitir; warning solo informa).

Un error de estructura (esquema) no guarda nada. Un error de negocio sí guarda el borrador, y tendrás que corregirlo antes de emitir.

Errores

HTTPcodeCausa
422missing_required_fieldsFalta invoice_series_code o meta.external_ref. El detalle trae missing.
422invoice_number_not_allowedHas enviado header.invoice_number.
422issue_date_not_allowedHas enviado issue_data.issue_date.
422inferred_fields_not_allowedHas enviado campos que deduce el sistema.
422issuer_tax_id_mismatchsupplier.tax_id no es el NIF de tu empresa.
422schema_validation_failedEl cuerpo no cumple el esquema. El detalle trae errors.
409invoice_update_not_allowedLa factura que intentas actualizar ya no es un borrador.
409invoice_race_conditionOtra petición ha escrito la misma factura a la vez. Reintenta.

Crear o actualizar en bloque

POST /api/v1.1/drafts/batch

{
  "batch_id": "lote-2026-11-01",
  "invoices": [
    {
      "schema_version": "json_1_1",
      "meta": { "direction": "issued", "source_system": "mi-erp", "external_ref": "PEDIDO-2026-001" },
      "invoice": { "...": "igual que en el alta individual" }
    },
    {
      "schema_version": "json_1_1",
      "meta": { "direction": "issued", "source_system": "mi-erp", "external_ref": "PEDIDO-2026-002" },
      "invoice": { "...": "igual que en el alta individual" }
    }
  ]
}
  • invoices: al menos una factura. Cada elemento lleva invoice y meta.external_ref.
  • batch_id y file_name: opcionales.
  • De meta solo se usan external_ref y source_system.
curl -X POST "https://app.faxtal.com/api/v1.1/drafts/batch" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \
  -H "Content-Type: application/json" \
  -d @lote.json

Cómo se procesa:

  • La llamada espera a que termine todo el lote.
  • Cada factura va por separado: si una se rechaza, las demás se guardan igual.
  • Si alguna factura trae campos que deduce el sistema, se rechaza el lote entero con 422 inferred_fields_not_allowed y la lista items (index, fields).
  • La respuesta detalla como máximo 500 facturas. Envía lotes de 500 o menos.

Respuesta 200:

{
  "batch_id": "000000000000000000000001",
  "status": "completed_with_errors",
  "total": 2,
  "accepted": 1,
  "rejected": 1,
  "items": [
    {
      "index": 0,
      "status": "accepted",
      "id": "00000000-0000-0000-0000-000000000001",
      "number": null,
      "errors": []
    },
    {
      "index": 1,
      "status": "rejected",
      "id": null,
      "number": null,
      "errors": [
        { "code": "missing_required_fields", "missing": ["invoice.header.invoice_series_code"] }
      ]
    }
  ]
}
  • batch_id: el identificador que asigna la plataforma, no el que envías.
  • status: completed si se aceptan todas, completed_with_errors si se rechaza alguna, failed si el lote no se pudo procesar.
  • items[].status: accepted o rejected. index es la posición en invoices.

:::warning Un elemento accepted puede haberse guardado con errores de negocio: errors solo recoge los rechazos. Antes de emitir, valida cada borrador con POST /api/v1.1/drafts/{invoice_id}/validate. :::


Validar

Un JSON, sin guardarlo

POST /api/v1.1/drafts/validate

Envía el mismo cuerpo que en el alta. No guarda nada.

curl -X POST "https://app.faxtal.com/api/v1.1/drafts/validate" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \
  -H "Content-Type: application/json" \
  -d @borrador.json

Respuesta 200:

{
  "is_valid": false,
  "errors": [
    {
      "source": "business",
      "code": "missing_required_field",
      "message": "...",
      "path": "/invoice/customer/tax_id",
      "extra": null,
      "severity": "error"
    }
  ],
  "data": { "...": "canónico normalizado" }
}
  • data: el canónico tal como lo normaliza el sistema. Es null si hay errores de esquema.
  • Los campos que deduce el sistema aparecen en errors con inferred_fields_not_allowed, en lugar de rechazar la petición.

Esta validación no aplica lo que depende de tu empresa (el grupo de impuesto por defecto y las reglas de VeriFactu). El alta y la emisión pueden devolver errores o avisos que aquí no aparecen.

Un borrador guardado

POST /api/v1.1/drafts/{invoice_id}/validate

Vuelve a validar el borrador y actualiza su resultado de validación. Sin cuerpo.

curl -X POST "https://app.faxtal.com/api/v1.1/drafts/00000000-0000-0000-0000-000000000001/validate" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"

Respuesta 200:

{
  "id": "00000000-0000-0000-0000-000000000001",
  "is_valid": true,
  "status": "draft",
  "errors": []
}

Consultar y listar

Un borrador

GET /api/v1.1/drafts/{invoice_id}

curl "https://app.faxtal.com/api/v1.1/drafts/00000000-0000-0000-0000-000000000001" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"

Respuesta 200, resumida:

{
  "id": "00000000-0000-0000-0000-000000000001",
  "direction": "issued",
  "status": "draft",
  "series": "A",
  "number": null,
  "issue_date": null,
  "external_id": "PEDIDO-2026-001",
  "customer_tax_id": "B10000016",
  "customer_name": "Cliente Prueba S.L.",
  "currency": "EUR",
  "total_base": 1000.0,
  "total_tax": 210.0,
  "total": 1210.0,
  "created_at": "2026-11-02T09:15:00",
  "updated_at": "2026-11-02T09:15:00",
  "corrections": null
}

external_id es tu meta.external_ref. Si la factura ya se emitió, esta ruta responde 404 invoice_not_found: consúltala en Facturas emitidas.

El canónico del borrador

GET /api/v1.1/drafts/{invoice_id}/canonical

Devuelve el JSON canónico tal y como lo guardó Faxtal: normalizado y con los campos que el sistema infiere (clasificación fiscal, taxable, exempt, is_simplified…). Es lo que se declara a Hacienda.

curl "https://app.faxtal.com/api/v1.1/api/v1.1/drafts/00000000-0000-0000-0000-000000000001/canonical" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"

:::warning No se reenvía tal cual La respuesta no vale como cuerpo de PUT /api/v1.1/drafts: ese endpoint rechaza los campos inferidos con 422 inferred_fields_not_allowed. Úsalo para leer y para conciliar, no para reeditar. :::

Si la factura no tiene canónico guardado, responde 409 canonical_not_available.

La lista

GET /api/v1.1/drafts

ParámetroDescripción
seriesCódigo de serie.
customer_tax_idNIF del cliente.
numberNúmero de factura exacto. Sirve para recuperar una factura cuyo id has perdido.
external_refTu referencia (meta.external_ref), exacta.
issue_date_from, issue_date_toRango de fecha de emisión (YYYY-MM-DD). Un borrador no tiene fecha, así que con estos filtros no aparece.
limitDe 1 a 500. Por defecto, 50.
offsetDesplazamiento. Por defecto, 0.
curl -G "https://app.faxtal.com/api/v1.1/drafts" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \
  -d series=A -d limit=100

Respuesta 200: items (borradores con la forma anterior), total, limit y offset.


Documentos adjuntos

Un borrador admite adjuntos sin efecto fiscal (un albarán, un pedido firmado). Siguen con la factura al emitirla, porque el id no cambia.

OperaciónRutaPermiso
ListarGET /api/v1.1/drafts/{invoice_id}/documentsinvoices:read
SubirPOST /api/v1.1/drafts/{invoice_id}/documents (multipart, campo file)invoices:write
DescargarGET /api/v1.1/drafts/{invoice_id}/documents/{doc_id}invoices:read
EliminarDELETE /api/v1.1/drafts/{invoice_id}/documents/{doc_id}invoices:write
curl -X POST "https://app.faxtal.com/api/v1.1/drafts/00000000-0000-0000-0000-000000000001/documents" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \
  -F "file=@albaran.pdf"

El fichero no puede pasar de 16 MB (413 file_too_large) ni estar vacío (422 empty_file). Una vez emitida, los mismos documentos se gestionan desde Facturas emitidas.


Eliminar

DELETE /api/v1.1/drafts/{invoice_id}

curl -X DELETE "https://app.faxtal.com/api/v1.1/drafts/00000000-0000-0000-0000-000000000001" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"

Respuesta 204, sin cuerpo. Se borran también los documentos adjuntos del borrador.

Si la factura ya no es un borrador, responde 409 invoice_delete_not_allowed. Una factura emitida se anula, no se borra: ver Facturas emitidas.


Ver el histórico

GET /api/v1.1/drafts/{invoice_id}/events

curl "https://app.faxtal.com/api/v1.1/drafts/00000000-0000-0000-0000-000000000001/events" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"

Devuelve una lista de eventos. Cada uno trae event_type, event_code, occurred_at, actor (user_id o api_key_id), from_status y to_status cuando hay cambio de estado, y details. Cuando el borrador se emite, sus eventos pasan a consultarse en GET /api/v1.1/issued-invoices/{invoice_id}/events.


Emitir un borrador

POST /api/v1.1/issued-invoices/{invoice_id}/issue, con el scope invoices:issue y sin cuerpo.

curl -X POST "https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001/issue" \
  -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"

La emisión vuelve a validar el borrador, le asigna el número de la serie y la fecha de hoy y lo registra en los sistemas fiscales que tengas activos. Si hay errores, no emite y el borrador sigue en draft. La respuesta y los errores están en Facturas emitidas.


Ver también

On this page