FactuPulse
Volver a FactuPulse

API para plataformas

Para marketplaces y aplicaciones donde varias empresas venden a sus clientes. Con esta API, cada venta genera automáticamente la factura de la empresa que vende, se envía por correo al comprador y puedes mostrarla dentro de tu aplicación.

Cómo funciona

1. Recibes una claveFactuPulse te da una clave de API para tu plataforma. Solo se usa desde tu servidor.
2. Das de alta a cada empresaCon una llamada creas su cuenta de FactuPulse, ya conectada y pagada con la tarjeta de tu plataforma. Hasta que está pagada no factura.
3. Facturas cada ventaPor cada pedido llamas a POST /v1/invoices. La factura sale a nombre de la empresa, numerada y lista.

La empresa que vende es siempre el emisor legal de la factura: sus datos fiscales, su numeración y su cuenta. Las facturas que crea tu plataforma van en una serie propia (por ejemplo MK2026-0001), separada de las que la empresa haga a mano, y la empresa las ve en su FactuPulse como cualquier otra. Tu plataforma solo puede ver y tocar las facturas que ella misma ha creado.

Las devoluciones y cancelaciones las gestiona tu aplicación; cuando una venta se anula, llamas a anular y FactuPulse emite la factura rectificativa, que es lo que exige la ley (una factura emitida no se borra).

Autenticación

URL base: https://factupulse.com/api/v1. Todas las peticiones y respuestas son JSON en UTF-8.

Envía la clave en la cabecera Authorization:

Authorization: Bearer fpk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Si algún proxy de tu lado elimina esa cabecera, también vale X-Api-Key: fpk_live_….

La clave es como una contraseña de todas las empresas conectadas. Guárdala en tu servidor (variable de entorno o gestor de secretos) y llama a la API solo desde ahí, nunca desde la app móvil ni desde el navegador. Si se filtra, pide una nueva: la anterior deja de funcionar al instante.

Todas las operaciones usan GET o POST, porque algunos proxies bloquean PUT y DELETE.

Límite: 3.000 peticiones cada 10 minutos por plataforma. Si lo superas recibes 429 rate_limited; espera y reintenta.

GET/v1/ping

Comprueba que la clave funciona.

{ "ok": true, "platform": "MercaApp", "time": "2026-10-05T10:00:00+02:00" }

Alta automática de empresas

Cuando un vendedor activa la facturación en tu app (o al darlo de alta), crea su cuenta de FactuPulse con sus datos. Queda conectada a tu plataforma al momento, sin que tenga que hacer nada en FactuPulse.

POST/v1/companies

Campo
referenceObligatorio. Tu identificador del vendedor. Si repites la llamada con la misma reference, no se crea otra cuenta: te devolvemos la existente con "duplicate": true.
termsAcceptedObligatorio, true. Confirmas que el vendedor ha aceptado los términos de uso y la política de privacidad de FactuPulse. Muéstraselo en tu pantalla de alta, por ejemplo con una casilla: «Acepto que FactuPulse emita mis facturas y sus términos y privacidad».
ownerObligatorio. Persona titular: name y email.
companyDatos fiscales del vendedor: legalName (obligatorio: razón social o nombre completo del autónomo), tradeName, nif, address, zip, city, province, country, phone, email. Para poder facturar hacen falta NIF y domicilio; si no los tienes ahora, el vendedor los puede completar en FactuPulse.
successUrl, cancelUrlOpcional, si paga cada empresa. Direcciones https:// de tu app a las que vuelve el vendedor después de pagar o si cancela el pago (ver cobro).
returnUrlOpcional. Solo se usa si el correo ya tenía cuenta (ver abajo).
POST /v1/companies
{
  "reference": "seller-77",
  "termsAccepted": true,
  "owner": { "name": "Joan Puig", "email": "joan@fornpuig.cat" },
  "company": { "legalName": "Forn Puig SL", "tradeName": "Forn Puig", "nif": "B12345674",
               "address": "C/ Major 1", "zip": "08001", "city": "Barcelona" }
}

201
{
  "id": 42, "reference": "seller-77", "name": "Forn Puig", "legalName": "Forn Puig SL", "nif": "B12345674",
  "ready": true, "plan": "pro", "paidBy": "platform",
  "subscription": { "status": "active", "renewsAt": "2026-11-05T18:04:15+01:00", "cancelAtPeriodEnd": false },
  "canInvoice": true, "price": 7, "connectedAt": "2026-10-05T18:04:15+02:00", "welcomeEmailSent": true
}

Si paga tu plataforma con tarjeta, el alta cobra el primer mes en el acto y solo responde 201 si el cobro se ha hecho (ver pago de las empresas). Si paga cada empresa, la respuesta trae checkoutUrl: lleva al vendedor a esa dirección justo después del alta.

Guarda id con tu vendedor: es el company de cada factura. Cuando la empresa queda pagada, la persona titular recibe un correo de bienvenida para elegir su contraseña; con ella puede entrar en FactuPulse cuando quiera a ver y descargar sus facturas, aunque no le hace falta para que tu plataforma facture.

Si el correo ya tiene cuenta en FactuPulse, no la tocamos (sería entrar en la cuenta de otra persona): recibes 409 account_exists con un connectUrl. Muéstraselo al vendedor; al abrirlo, entra con su cuenta y pulsa «Conectar» (ver empresas que ya tienen cuenta).

409
{ "error": { "code": "account_exists", "message": "Ese correo ya tiene cuenta en FactuPulse…",
             "connectUrl": "https://factupulse.com/?connect=Qm3k…" } }

GET/v1/companies/{id}

Estado de una empresa. Los mismos campos salen, para todas, en GET /v1/companies.

Campo
readyTiene completos sus datos fiscales (razón social, NIF y domicilio).
planpro o basic.
paidByplatform (la pagas tú) o company (la paga ella). Lo acordamos contigo al darte de alta.
subscriptionEl pago mensual de esa empresa: status (active, past_due si está reintentando un cobro, canceled…), renewsAt y cancelAtPeriodEnd, o null.
canInvoiceResumen: true si ahora mismo puedes emitir sus facturas.
priceLo que cuesta esa empresa al mes en euros, IVA incluido.

Pago de las empresas

Cada empresa dada de alta se paga cada mes, sin periodo de prueba, al precio acordado con FactuPulse para tu plataforma. Según lo acordado, paga tu plataforma (lo habitual) o cada empresa. GET /v1/billing te dice cuál es tu caso.

Si paga tu plataforma con su tarjeta

  1. Una sola vez: guarda la tarjeta de tu empresa. Pide el enlace con POST /v1/billing/setup-link (o te lo enviamos nosotros), ábrelo y añade la tarjeta en la página segura de Stripe. Sirve también para cambiarla cuando caduque.
  2. Cada alta (POST /v1/companies) se cobra al momento a esa tarjeta: el primer mes en el acto y después cada mes en la misma fecha. La respuesta solo llega con 201 cuando el cobro se ha hecho, y desde ese momento ya puedes emitir sus facturas (canInvoice: true).
  3. Si no hay tarjeta guardada, no se crea la cuenta: recibes 402 payment_method_required con un setupUrl para añadirla.
  4. Si el cobro falla (tarjeta rechazada, sin fondos…), recibes 402 payment_failed con el company creado, pero no podrás emitir sus facturas hasta que se cobre. Resuélvelo y repite la misma petición de alta (misma reference) o llama a POST /v1/companies/{id}/activate.
  5. Si más adelante falla una renovación, Stripe reintenta el cobro durante unos días. Si al final no se cobra, la suscripción se cancela y sus facturas reciben 402 subscription_required; reactívala con POST /v1/companies/{id}/activate.
  6. Cuando un vendedor deja tu plataforma, llama a POST /v1/companies/{id}/disconnect: deja de cobrarse al momento y se desconecta. Sus facturas se quedan en su cuenta de FactuPulse. Si es el vendedor quien desconecta tu plataforma desde FactuPulse, también deja de cobrarse.

Las facturas de estos cobros te llegan de FactuPulse por correo, una por empresa y mes, con el nombre y NIF de la empresa en la descripción.

POST /v1/billing/setup-link
{ "successUrl": "https://merca.app/admin/facturacion", "cancelUrl": "https://merca.app/admin/facturacion" }
200 { "url": "https://checkout.stripe.com/c/pay/cs_live_…" }

GET /v1/billing
200 { "paidBy": "platform", "chargedToCard": true, "card": "Visa ····4242 (caduca 12/2030)",
      "pricePerCompany": 7, "paidCompanies": 12, "monthlyTotal": 84, "currency": "EUR" }

POST /v1/companies   (sin tarjeta)
402 { "error": { "code": "payment_method_required", "message": "…", "setupUrl": "https://checkout.stripe.com/…" } }

POST /v1/companies   (cobro rechazado)
402 { "error": { "code": "payment_failed", "message": "No se ha podido cobrar la empresa a tu tarjeta: …", "company": 42 } }

POST /v1/companies/42/disconnect
200 { "ok": true, "company": 42, "disconnected": true }

Si paga cada empresa

Cada vendedor paga su suscripción mensual con su tarjeta. Sus facturas solo se emiten mientras esté activa; si no, POST /v1/invoices devuelve 402 subscription_required. El alta (POST /v1/companies) ya te devuelve el enlace de pago en checkoutUrl: lleva al vendedor a él. Si no paga en ese momento, o más adelante falla un cobro, pide otro enlace:

POST/v1/companies/{id}/checkout

Campo
successUrl, cancelUrlDirecciones https:// de tu app a las que vuelve el vendedor después de pagar o si cancela.
POST /v1/companies/42/checkout
{ "successUrl": "https://merca.app/vendedores/77/ok", "cancelUrl": "https://merca.app/vendedores/77" }

200
{ "url": "https://checkout.stripe.com/c/pay/cs_live_…", "price": 7, "interval": "month", "currency": "EUR" }

Abre url en el navegador (o en un navegador dentro de tu app). Es la página de pago segura de Stripe: el vendedor pone su tarjeta y sus datos de facturación, se le cobra el primer mes al momento y la suscripción se renueva sola cada mes. El enlace caduca en 24 horas, así que pídelo justo antes de usarlo. Sus facturas de la suscripción le llegan de FactuPulse por correo, y puede cambiar de tarjeta o darse de baja desde FactuPulse → Plan.

La activación tarda unos segundos en reflejarse después del pago. Al volver a tu successUrl, consulta GET /v1/companies/{id} hasta que canInvoice sea true. Si más adelante un cobro falla o se da de baja, canInvoice vuelve a false y tus facturas para ella reciben 402 subscription_required: avísale y vuelve a pedirle el pago.

Consejo: no pierdas pedidos mientras tanto. Si recibes 402 o 422 company_incomplete, deja la factura en tu cola y reinténtala (con el mismo orderId) cuando canInvoice sea true.

Empresas que ya tienen cuenta de FactuPulse

Si el vendedor ya usa FactuPulse, no hace falta crearle otra cuenta: genera un enlace de conexión y que la autorice él.

POST/v1/connect-links

Crea un enlace de conexión. Válido 30 días y de un solo uso.

CampoTipo
referencetextoOpcional. Tu identificador de esa empresa (por ejemplo, el id de vendedor). Te lo devolvemos al conectar y en /v1/companies.
emailtextoOpcional. Correo de la empresa: se rellena en el formulario de acceso.
returnUrltextoOpcional. Dirección https:// a la que volvemos tras conectar.
POST /v1/connect-links
{ "reference": "seller-77", "email": "joan@fornpuig.cat", "returnUrl": "https://merca.app/vendedores/77/facturacion" }

201
{ "url": "https://factupulse.com/?connect=Qm3k…", "expiresAt": "2026-11-04T10:00:00+01:00" }

Muestra ese enlace a la empresa (un botón «Activar facturación con FactuPulse», por ejemplo). Al abrirlo:

  1. Entra con su cuenta (si no la tuviera, también puede registrarse desde ahí).
  2. Ve qué podrá hacer tu plataforma y pulsa Conectar. Solo la persona titular de la empresa puede hacerlo.
  3. Si indicaste returnUrl, la devolvemos ahí con estos parámetros:
https://merca.app/vendedores/77/facturacion?company=42&reference=seller-77&status=connected

Guarda company (el id de la empresa en FactuPulse) junto a tu vendedor: lo usarás en cada factura. No te fíes solo de esa redirección; compruébalo con /v1/companies.

La empresa puede desconectar tu plataforma cuando quiera desde Ajustes → Conexiones. A partir de ese momento tus llamadas para ella devuelven 403 company_not_connected.

Consulta su estado con GET /v1/companies/{id} como cualquier otra empresa. Si pagas tú con tarjeta, se cobra al conectar; si no aparece canInvoice: true, llama a POST /v1/companies/{id}/activate. Si paga cada empresa, pídele el pago.

Crear una factura

POST/v1/invoices

Crea y emite la factura en el acto (con número definitivo y fecha de hoy). Llámalo cuando el pedido esté pagado o confirmado.

CampoTipo
companynúmeroObligatorio. Id de la empresa que vende.
orderIdtextoObligatorio. Tu número de pedido (hasta 120 caracteres). Si repites un orderId no se crea otra factura: te devolvemos la existente con "duplicate": true. Así puedes reintentar sin miedo.
customerobjetoEl comprador: name, email, nif, address, zip, city, province, country (código de 2 letras, por defecto ES).
lineslistaObligatorio. Cada línea: description, quantity (por defecto 1), unitPrice, vat (porcentaje: 21, 10, 4, 0; por defecto 21), discount (porcentaje, opcional).
pricesIncludeTaxsí/noSi es true, unitPrice lleva el IVA incluido (lo normal en venta a particulares). Por defecto false: precios sin IVA. Ver precios con IVA incluido.
paidsí/noSi es true, la factura queda como cobrada. Opcionales: paidAt (AAAA-MM-DD) y paymentMethod (por defecto «Tarjeta»).
sendEmailsí/noSi es true, enviamos la factura en PDF al correo del comprador, en nombre de la empresa.
languagetextoIdioma de la factura y del correo: es (por defecto), en, ca, gl, eu.
notestextoOpcional. Aparece en la factura.

Particular o empresa: el tipo de factura sale solo

Consejo: en tu checkout, ofrece una casilla «Necesito factura a nombre de empresa» que pida razón social, NIF y dirección.

Ejemplo: particular, precios con IVA, cobrada y enviada por correo

POST /v1/invoices
{
  "company": 42,
  "orderId": "A-10045",
  "pricesIncludeTax": true,
  "paid": true,
  "sendEmail": true,
  "customer": { "name": "Laura Gómez", "email": "laura@example.com" },
  "lines": [
    { "description": "Camiseta algodón", "quantity": 2, "unitPrice": 12.10, "vat": 21 },
    { "description": "Envío", "quantity": 1, "unitPrice": 3.99, "vat": 21 }
  ]
}
201
{
  "id": "a7a6e0f0-f048-41dc-ae3f-e19dc2ad6693",
  "number": "MK2026-0001",
  "orderId": "A-10045",
  "company": 42,
  "type": "simplified",
  "status": "issued",
  "issueDate": "2026-10-05",
  "currency": "EUR",
  "language": "es",
  "customer": { "name": "Laura Gómez", "nif": "", "email": "laura@example.com" },
  "lines": [
    { "description": "Camiseta algodón", "quantity": 2, "unitPrice": 10, "vat": 21, "discount": 0, "base": 20 },
    { "description": "Envío", "quantity": 1, "unitPrice": 3.3, "vat": 21, "discount": 0, "base": 3.3 }
  ],
  "taxes": [ { "rate": 21, "base": 23.3, "amount": 4.89 } ],
  "subtotal": 23.3, "tax": 4.89, "withholding": 0, "total": 28.19, "paid": 28.19,
  "pdfUrl": "https://factupulse.com/api/public/VK9SJ_b1TDZf06WMxhhFeJzp08Ip462C/pdf",
  "emailedAt": "2026-10-05T17:54:38+02:00",
  "rectifies": null, "cancelledBy": null,
  "chargedTotal": 28.19, "roundingDifference": 0
}

Guarda id, number y pdfUrl con tu pedido. Si el envío del correo falla, la factura se crea igualmente y la respuesta incluye emailError; puedes reintentarlo con reenviar.

Ejemplo: empresa (factura completa)

{
  "company": 42,
  "orderId": "A-10046",
  "sendEmail": true,
  "customer": { "name": "Talleres Ruiz SL", "nif": "B87654323", "address": "Av. Diagonal 100",
                "zip": "08019", "city": "Barcelona", "email": "compras@ruiz.es" },
  "lines": [ { "description": "Taladro percutor", "quantity": 1, "unitPrice": 100, "vat": 21 } ]
}

Precios con IVA incluido

Con pricesIncludeTax: true, FactuPulse calcula la base imponible de cada línea para que la factura sume lo que pagó el cliente. Si una línea no cuadra al céntimo con su cantidad (por ejemplo, 3 unidades a 9,99 €), se factura como una sola línea por el importe total, con la cantidad en la descripción («3 × Taza»).

La ley exige calcular el IVA sobre la suma de bases de cada tipo y redondearlo al céntimo. Por eso hay importes con IVA incluido que ninguna base puede dar exactamente: por ejemplo, 16,05 € al 21 % (13,26 € + 2,78 € = 16,04 €; 13,27 € + 2,79 € = 16,06 €). En esos casos la factura difiere en un céntimo de lo cobrado. La respuesta te lo indica siempre:

Si necesitas que coincida siempre al céntimo, envía los precios sin IVA (pricesIncludeTax: false) y cobra al cliente el total que devuelve la factura.

Mostrar la factura en tu app

pdfUrl es un enlace privado y permanente al PDF que no necesita clave: puedes abrirlo directamente en la app del comprador (un botón «Ver factura») o en un visor web. Trátalo como un dato privado del comprador: no lo publiques.

Si prefieres servir el PDF desde tu servidor, usa la clave:

GET/v1/invoices/{id}/pdf?company=42

Devuelve el PDF (application/pdf). Añade &download=1 para que se descargue con su nombre de archivo.

Consultar y reenviar

GET/v1/invoices?company=42

Facturas creadas por tu plataforma para esa empresa, de la más reciente a la más antigua. Filtros: orderId, from y to (AAAA-MM-DD), limit (1–100, por defecto 50), offset.

GET /v1/invoices?company=42&orderId=A-10045
{ "data": [ { "id": "a7a6e0f0-…", "number": "MK2026-0001", … } ] }

GET/v1/invoices/{id}?company=42

Una factura (ver objeto factura).

POST/v1/invoices/{id}/send

{ "company": 42, "email": "otra@direccion.com" }

Envía (o reenvía) la factura por correo. Sin email, se usa el del comprador.

Anular una factura (devoluciones y cancelaciones)

POST/v1/invoices/{id}/cancel

{ "company": 42, "reason": "Devolución del pedido A-10045", "sendEmail": true }

Emite una factura rectificativa por el importe total, en la serie de rectificativas de la empresa (por ejemplo R2026-0003), y devuelve la factura original con "status": "cancelled" y los datos de la rectificativa en cancelledBy. Con sendEmail, la rectificativa se envía al comprador. Si llamas dos veces, no se duplica ("duplicate": true).

Para una devolución parcial, de momento anula la factura y crea una nueva con lo que se queda el cliente y un orderId distinto (por ejemplo A-10045-2).

Objeto factura

Campo
idIdentificador de la factura en FactuPulse.
numberNúmero legal (serie + año + correlativo).
orderIdTu número de pedido.
companyId de la empresa emisora.
typeinvoice (completa), simplified o rectifying.
statusissued o cancelled.
issueDate, currency, languageFecha de emisión, moneda e idioma.
customername, nif, email.
linesLíneas tal como figuran en la factura (precios sin IVA) con su base.
taxesDesglose por tipo: rate, base, amount.
subtotal, tax, withholding, totalBase imponible, impuestos, retención y total, en euros.
paidImporte cobrado.
pdfUrlEnlace privado al PDF.
emailedAtCuándo se envió por correo por última vez, o null.
rectifies, cancelledByFactura a la que rectifica / rectificativa que la anula.

Los importes son números con dos decimales como máximo, en euros.

Errores

Los errores devuelven un código HTTP y este cuerpo; message está en español y se puede mostrar al vendedor:

{ "error": { "code": "company_incomplete", "message": "Antes de emitir facturas completa los datos de tu empresa: NIF, domicilio fiscal." } }
HTTPcodeQué hacer
400company_required, order_id_required, lines_required, invalid_line, email_required, invalid_return_urlFalta un dato o está mal formado. Corrige la petición.
401unauthorizedClave ausente, incorrecta o desactivada.
400reference_required, terms_required, owner_name_required, owner_email_invalid, legal_name_requiredFaltan datos para dar de alta la empresa.
402payment_method_requiredNo hay tarjeta guardada: añádela en setupUrl y repite.
402payment_failedNo se pudo cobrar el alta. Repite la misma petición o usa /activate cuando esté resuelto.
402subscription_requiredEsa empresa no está pagada ahora mismo. Ver pago de las empresas.
402plan_limitLa empresa ha llegado al límite de su plan.
409account_existsEse correo ya tiene cuenta: muestra connectUrl al vendedor.
409already_subscribed, paid_by_platform, not_card_billingEsa operación no aplica a tu forma de pago.
403company_not_connectedLa empresa no te ha autorizado o lo ha retirado. Pídele que vuelva a conectar.
404company_not_found, invoice_not_foundNo existe o no la creó tu plataforma.
422company_incompleteLa empresa debe completar sus datos fiscales en FactuPulse. Reintenta después con el mismo orderId.
422invalid_nif, invalid_customer, invalid_invoice, cannot_cancelDatos que la ley no admite (NIF incorrecto, simplificada de más de 3.000 €…). Lee message.
429rate_limitedDemasiadas peticiones. Espera y reintenta.
502email_failedNo se pudo enviar el correo. La factura existe; reintenta el envío.
502 / 503billing_error, billing_unavailableNo se pudo crear el enlace de pago. Reintenta más tarde.
5xx—Error temporal. Reintenta con el mismo orderId (no se duplicará).

Buenas prácticas

Prueba rápida

export FP_KEY="fpk_live_…"

curl https://factupulse.com/api/v1/ping -H "Authorization: Bearer $FP_KEY"

curl https://factupulse.com/api/v1/companies -H "Authorization: Bearer $FP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reference":"prueba-1","termsAccepted":true,
       "owner":{"name":"Tu nombre","email":"tu@correo.com"},
       "company":{"legalName":"Empresa de prueba SL","nif":"B12345674","address":"C/ Prueba 1","zip":"28001","city":"Madrid"}}'

curl https://factupulse.com/api/v1/invoices -H "Authorization: Bearer $FP_KEY" \
  -H "Content-Type: application/json" \
  -d '{"company":42,"orderId":"PRUEBA-1","pricesIncludeTax":true,
       "customer":{"name":"Cliente de prueba","email":"tu@correo.com"},
       "lines":[{"description":"Producto de prueba","unitPrice":12.10,"vat":21}],
       "sendEmail":true}'

Para hacer pruebas, da de alta una empresa de prueba con un correo vuestro, como en el ejemplo. Ten en cuenta que el alta se cobra como cualquier otra: cuando termines, desconéctala con POST /v1/companies/{id}/disconnect para que no se renueve. Las facturas emitidas no se pueden borrar (solo anular), como exige la ley.

¿Dudas o necesitas otra función? Escríbenos desde la página de contacto.