Ir al contenido

Recetario ​

Ejemplos copia-y-pega de los flujos más habituales. Sustituye bk_live_xxx por tu key y los … por los UUID de tu tenant. Todos los importes van como string. Si usas OAuth2, sustituye bk_live_xxx por tu access token bo_at_… en los mismos ejemplos. Consulta Autenticación y scopes.

Comprobar la key ​

bash
curl https://api.test.bilteo.com/api/v1/ping \
  -H "Authorization: Bearer bk_live_xxx"
# → { "pong": true }

Descubrir lo que necesitas ​

Antes de facturar, localiza los IDs de tu tenant:

bash
# Impuestos de venta (para main_tax_id / retention_tax_id / surcharge_tax_id)
curl "https://api.test.bilteo.com/api/v1/taxes?tax_use=sale&is_active=true" \
  -H "Authorization: Bearer bk_live_xxx"

# Series y actividades (opcional: si los omites, se aplican los defaults)
curl https://api.test.bilteo.com/api/v1/series              -H "Authorization: Bearer bk_live_xxx"
curl https://api.test.bilteo.com/api/v1/economic-activities -H "Authorization: Bearer bk_live_xxx"

F1 ordinaria: crear y emitir ​

bash
# 1) Crear el borrador (serie/actividad/moneda por defecto si se omiten)
curl -X POST https://api.test.bilteo.com/api/v1/invoices \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{
    "type": "F1",
    "partner_id": "…",
    "operation_date": "2026-01-15",
    "lines": [
      { "description": "Consultoría enero", "quantity": "10", "unit_price": "75.00",
        "main_tax_id": "…iva21…" }
    ]
  }'
# → 201, { "id": "INV…", "state": "draft", "total": "907.50", … }

# 2) Emitir: numera, congela importes y encola la remisión a la AEAT
curl -X POST https://api.test.bilteo.com/api/v1/invoices/INV…/actions/issue \
  -H "Authorization: Bearer bk_live_xxx"
# → 200, { "state": "issued", "number": "2026/0001", … }

F2 simplificada (sin receptor) ​

bash
curl -X POST https://api.test.bilteo.com/api/v1/invoices \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{ "type": "F2",
        "lines": [{ "description": "Venta mostrador", "quantity": "1",
                    "unit_price": "30.00", "main_tax_id": "…iva21…" }] }'

Varias líneas, descuento y dos tipos de IVA ​

El descuento es un porcentaje (0–100) por línea. Cada línea puede llevar un main_tax_id distinto: el desglose se agrupa por tipo.

bash
curl -X POST https://api.test.bilteo.com/api/v1/invoices \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{
    "type": "F1", "partner_id": "…",
    "lines": [
      { "description": "Servicio (21%)", "quantity": "1", "unit_price": "1000.00",
        "discount": "10", "main_tax_id": "…iva21…" },
      { "description": "Libro (4%)",      "quantity": "2", "unit_price": "20.00",
        "main_tax_id": "…iva4…" }
    ]
  }'

Con retención de IRPF ​

Añade retention_tax_id (un impuesto type: "retencion"). Resta del total a pagar:

bash
curl -X POST https://api.test.bilteo.com/api/v1/invoices \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{ "type": "F1", "partner_id": "…",
        "lines": [{ "description": "Servicio profesional", "quantity": "1",
                    "unit_price": "1000.00",
                    "main_tax_id": "…iva21…", "retention_tax_id": "…irpf15…" }] }'
# total = 1210.00, total_payable = 1060.00 (1000 + 210 IVA − 150 IRPF)

Con recargo de equivalencia ​

Añade surcharge_tax_id (un impuesto type: "recargo") en las líneas afectadas. Es por línea, no por factura.

bash
"lines": [{ "description": "Mercancía", "quantity": "1", "unit_price": "100.00",
            "main_tax_id": "…iva21…", "surcharge_tax_id": "…re52…" }]

Exenta, no sujeta, inversión del sujeto pasivo ​

A diferencia de Verifactu Direct API, aquí la clasificación fiscal no va en la petición: la lleva el impuesto que pones en main_tax_id. Localiza en GET /taxes el de la familia (type) correcta:

OperaciónImpuesto (type) a elegir
Exenta (art. 20: sanidad, educación, seguros…)exento (con su exempt_reason)
No sujetano_sujeto / no_sujeto_loc
Inversión del sujeto pasivo (ISP)sujeto_isp
bash
# Exenta: el impuesto exento lleva cuota 0 y su motivo de exención
-d '{ "type": "F1", "partner_id": "…",
      "lines": [{ "description": "Formación bonificada", "quantity": "1",
                  "unit_price": "500.00", "main_tax_id": "…exento-art20…" }] }'

# Inversión del sujeto pasivo: el impuesto sujeto_isp no repercute IVA
-d '{ "type": "F1", "partner_id": "…",
      "lines": [{ "description": "Ejecución de obra", "quantity": "1",
                  "unit_price": "1200.00", "main_tax_id": "…isp…" }] }'

Intracomunitaria y exportación ​

La diferencia la marcan el país del cliente (en /partners) y el impuesto:

OperaciónPaís del clienteImpuesto
Entrega intracom. de bienes (exenta art. 25)otro país UE, con VAT-IESexento (art. 25)
Servicios B2B intracom. (no sujetos por localización)otro país UEno_sujeto_loc
Exportación (fuera de la UE, exenta art. 21)país no UEexento (art. 21)
bash
# 1) Cliente UE con su VAT-IES intracomunitario
curl -X PUT "https://api.test.bilteo.com/api/v1/partners/by-external-id/EU-FR-01" \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{ "type": "com", "name": "Atelier Vionnet SARL",
        "id_num": "FR47812904563", "country": "FR" }'

# 2) Entrega intracomunitaria de bienes (exenta)
curl -X POST https://api.test.bilteo.com/api/v1/invoices \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{ "type": "F1", "partner_id": "…id-del-cliente-UE…",
        "lines": [{ "description": "Suministro de material", "quantity": "1",
                    "unit_price": "800.00", "main_tax_id": "…exento-art25…" }] }'

Bilteo traduce impuesto + país al registro Veri*Factu correcto (exención, no sujeción por localización…). Detalle conceptual en Impuestos.

Cerrar el bucle Veri*Factu ​

La remisión es asíncrona. Lo recomendado es suscribirte a webhooks (verifactu.accepted / verifactu.rejected) y dejar que Bilteo te avise. Si prefieres consultar, sondea el registro hasta que deje de estar pendiente:

bash
curl "https://api.test.bilteo.com/api/v1/verifactu-records?invoice_id=INV…" \
  -H "Authorization: Bearer bk_live_xxx"
# state: pending → in-process → accepted | accepted_error | rejected | unknown_response

Un registro con respuesta firme (rejected, accepted, accepted_error) puede subsanarse.

Rectificar una factura emitida ​

bash
# Por sustitución, error de derecho (F1 → R1). Crea la R1 en borrador.
curl -X POST https://api.test.bilteo.com/api/v1/invoices/INV…/actions/rectify \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{ "rectificative_type": "S", "rectification_cause": "law_error" }'
# → 201, { "type": "R1", "state": "draft", … } → edítala y emítela

Detalle del mapeo causa → R-type en Corregir o anular.

Convertir una simplificada en completa (F3) ​

Desde una F2 emitida, crea su sustitutiva (F3) para un cliente identificado (asistente "sustitutiva"); queda en borrador para revisar y emitir:

bash
curl -X POST https://api.test.bilteo.com/api/v1/invoices/INV_F2…/actions/replace \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{ "partner_id": "…" }'
# → 201, { "type": "F3", "state": "draft", … }
# (?issue=true para emitir en la misma llamada; requiere invoices:issue)

Subsanar un registro ​

Operación extraordinaria

Muy poco frecuente y fuera del flujo normal: solo cuando la AEAT rechazó un registro por un error de remisión (normalmente un bug de integración). No es una rectificativa.

Requiere invoices:fix e invoices:issue. El resultado depende del historial:

bash
curl -X POST https://api.test.bilteo.com/api/v1/invoices/INV…/actions/request-fix \
  -H "Authorization: Bearer bk_live_xxx"
# Nunca aceptada (último registro rejected):
#   → 200, state: needs_fix → corrige las líneas → vuelve a emitir (issue)
# Alguna vez aceptada (accepted | accepted_error):
#   → 200, state: issued (sin cambios) y se encola la subsanación con los
#     datos actuales del cliente: edítalos ANTES con PATCH /partners/{id}

Flujo completo en Subsanación de errores.

Anular ​

Operación poco habitual

Solo para una factura emitida por error (que nunca debió existir), no para corregir una factura válida — para eso, una rectificativa. No es parte del día a día.

bash
curl -X POST https://api.test.bilteo.com/api/v1/invoices/INV…/actions/cancel \
  -H "Authorization: Bearer bk_live_xxx"

Sincronizar clientes y productos (external_id) ​

Mantén tus clientes y productos sincronizados desde cualquier sistema (ERP, e-commerce, TPV, tu propia app…) usando tu propio identificador (external_id) con el PUT upsert: mandas siempre el mismo PUT con el estado actual y te olvidas de si ya existía en Bilteo. Reejecutar es seguro — 201 si lo creó, 200 si lo actualizó —, así no duplicas ni mantienes una tabla de equivalencias con los UUID de Bilteo. Detalle y resto de operaciones (GET/PATCH/DELETE/búsqueda) en Convenciones → Sincronización por external_id.

bash
# Cliente (partner)
curl -X PUT "https://api.test.bilteo.com/api/v1/partners/by-external-id/CLI-42" \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{ "type": "com", "name": "Comercial Aldealta SL", "id_num": "B85447761",
        "country": "ES" }'

# Producto — idéntico, sobre /products (lleva sus impuestos; la línea los hereda al usar product_id)
curl -X PUT "https://api.test.bilteo.com/api/v1/products/by-external-id/SKU-9" \
  -H "Authorization: Bearer bk_live_xxx" -H "Content-Type: application/json" \
  -d '{ "name": "Suscripción anual", "unit_price": "240.00", "main_tax_id": "…iva21…" }'

Después, al crear facturas, referencia el producto por su UUID de Bilteo (el que devuelve el PUT) o resuélvelo con GET /products/by-external-id/SKU-9.

Descargar la factura ​

bash
# PDF (por defecto) o HTML según el Accept
curl "https://api.test.bilteo.com/api/v1/invoices/INV…/actions/print" \
  -H "Authorization: Bearer bk_live_xxx" -H "Accept: application/pdf" -o factura.pdf

# Facturae 3.2.x (XML)
curl "https://api.test.bilteo.com/api/v1/invoices/INV…/actions/facturae" \
  -H "Authorization: Bearer bk_live_xxx" -o factura.xml

Bilteo · Documentación