Faxtal

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_YYYYYYYYYYYYYYYY es una API key ficticia: pon la tuya.
  • Los identificadores, como 00000000-0000-0000-0000-000000000001, son ficticios.
  • Los ficheros .json contienen facturas en formato canónico.

Emitir y enviar una factura

Scopes: invoices:write, invoices:issue, invoices:read y compliance:read.

  1. Guarda el borrador con PUT /api/v1.1/drafts. No envíes header.invoice_number ni issue_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.json

    Respuesta 201: {"success": true, "id": "00000000-0000-0000-0000-000000000001", "errors": []}. Si success es false, el borrador se ha guardado con errores: corrígelos y reenvíalo con ?invoice_id=00000000-0000-0000-0000-000000000001.

  2. 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 con status: "issued", number e issue_date. El id no cambia. Si no supera la validación, 422 con validation_failed y errors[].

  3. 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: el estado empieza en pending.

    curl https://app.faxtal.com/api/v1.1/invoices/00000000-0000-0000-0000-000000000001/verifactu/records \
      -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"
  4. 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
  5. Envíala por email con POST /api/v1.1/issued-invoices/{invoice_id}/send. Sin to, 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, 422 con no_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.

  1. Guarda el borrador con PUT /api/v1.1/drafts, sin header.invoice_number ni issue_data.issue_date, como en la receta anterior. En header.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
  2. Emite con tu número enviando invoice_number en el cuerpo de POST /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 con status: "issued" y number: "A-0042". Si el número no encaja con la plantilla de la serie, 422 con forced_number_invalid. Si ya lo tiene otra factura de la serie, 409 con forced_number_in_use. Si el borrador no supera la validación, 422 con validation_failed, y el número no se usa.

  3. 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.

Guías: Emitidas y Conceptos.

Registrar recibidas con reconocimiento automático

Scopes: received:write e invoices:read.

  1. 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 campo file. 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) y warnings. Cada documento consume el cupo mensual de reconocimiento del plan. Al agotarlo, 402 con quota_exceeded.

  2. Revisa la propuesta en tu sistema. Mira score y warnings, corrige lo que haga falta y guarda canonical_draft en recibida.json. Quita header.is_simplified y header.is_corrective: los calcula el sistema y la API los rechaza. Después, valídala sin guardar con POST /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.json

    Respuesta 200: is_valid y errors[]. Un error inferred_fields_not_allowed señala en path otro campo que debes quitar.

  3. 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.json

    Respuesta 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.

  4. 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

  1. Pide la rectificativa con POST /api/v1.1/issued-invoices/{invoice_id}/corrective, sobre el id de 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}]}'
    CampoDescripción
    correction_typeS (sustitución) o D (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 201 con el id del 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_invalid con el motivo en reason (por ejemplo lines_required_for_differences).

  2. Repásala y ajústala si hace falta, con GET /api/v1.1/drafts/{id}/canonical para ver qué ha quedado y PUT /api/v1.1/drafts?invoice_id={id} para cambiarlo. Con S (sustitución) la rectificativa hereda las líneas de la original: si la sustitución cambia el contenido, manda tú las líneas nuevas.

  3. 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 permiso invoices:issue.

Camino manual

  1. Localiza la factura original con GET /api/v1.1/issued-invoices/{invoice_id}. Anota su number y su issue_date.

    curl https://app.faxtal.com/api/v1.1/issued-invoices/00000000-0000-0000-0000-000000000001 \
      -H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY"
  2. Guarda la rectificativa como borrador con PUT /api/v1.1/drafts. En header, usa invoice_type_code "03", o "04" si la original es simplificada, y añade el bloque corrective:

    "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_number e invoice_issue_date: los de la original.
    • reason_code: el código del motivo, de "01" a "25".
    • correction_type: D (por diferencias) o S (por sustitución). Por diferencias, las líneas y los totales recogen solo la diferencia. Por sustitución, añade corrected_taxable_base y corrected_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.json

    Respuesta 201: {"success": true, "id": "00000000-0000-0000-0000-000000000004", "errors": []}. Si falta algún dato de la rectificación, success es false y errors[] trae códigos como corrective_reason_required.

  3. 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"
  4. Comprueba el vínculo con GET /api/v1.1/issued-invoices/{invoice_id}. En la rectificativa, corrections.corrects identifica la original: corrected_number y, si está en Faxtal, corrected_invoice_id. En la original, corrections.corrected_by lista 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.

  1. Prepara las facturas en formato canónico con su número (header.invoice_number) y su fecha (issue_data.issue_date). Pon tu referencia en meta.external_ref.

  2. 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.json

    Respuesta 200: is_valid y errors[].

  3. Importa las emitidas por lotes con POST /api/v1.1/issued-invoices/import/batch. El cuerpo lleva un batch_id tuyo y la lista invoices:

    {
      "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.json

    Respuesta 200, al terminar el lote: status (completed, completed_with_errors o failed), total, accepted, rejected e items[], con index, status, id, number y errors de 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.

  4. 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.

  5. Importa las recibidas una a una: no hay lote. Con JSON canónico, usa PUT /api/v1.1/received-invoices. Con XML, usa POST /api/v1.1/received-invoices/import con el formato en format (facturae_3_2_2, facturae_3_2_1, facturae_3_2, ubl_2_1 o cii_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.xml

    Respuesta 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.

  1. Emite cada borrador con POST /api/v1.1/issued-invoices/{invoice_id}/issue. La API pública no emite en bloque. Guarda el id y el number de 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"
  2. Consulta el registro de cada factura con GET /api/v1.1/invoices/{invoice_id}/verifactu/records. Mientras el estado sea pending, 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"
  3. Revisa las incidencias con GET /api/v1.1/verifactu/incidents. Cada incidencia trae num_serie, estado_registro, codigo_error, descripcion_error y submission_id. Cruza num_serie con el number de tus facturas. limit fija 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"
  4. 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.

  1. Crea una API key en cada empresa. Cada clave trabaja sobre una sola empresa, que la API deduce de la clave. Ver autenticación.

  2. Comprueba a qué empresa apunta cada clave con GET /api/v1.1/company. Guarda en tu integración la relación entre el tax_id de 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_id y el resto de datos de la empresa. GET /api/v1.1/companies devuelve una lista con esa misma empresa.

  3. 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
  4. Trata cada empresa por separado. Cada clave tiene su propio límite de peticiones, y cada empresa, su plan: un 402 en una empresa no afecta a las demás.

Guías: Multiempresa, Autenticación y Errores.

On this page