API de generación de facturas

Genera facturas PDF profesionales de forma programática desde tu propio servidor, ideal para sistemas internos, marketplaces o productos SaaS.

1. Autenticación#

Crea una API key en app.invosend.com/settings/api-keys. Se requiere un workspace de pago y rol owner.

Envía la clave como Bearer token en el header Authorization de cada request:

Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Las claves faltantes, mal formadas, inválidas o revocadas se rechazan con 403. También se rechazan si el workspace está en plan gratuito, suspendido o vencido.

2. Endpoint#

POST/api/invoices/generate-external

Base URL: https://app.invosend.com

Descripción de la API legible por máquina: OpenAPI 3.1 y Postman collection. Importe cualquiera de las dos en Postman, Insomnia o un generador de SDK.

3. Request Body#

CampoTipoRequeridoDescripción
customer_namestringSíNombre del cliente o destinatario de la factura.
customer_emailstringNoDirección de correo válida. La factura creada con esta llamada no se envía por correo (esta API no envía correos). Obligatorio si la factura es recurrente: cada factura generada después se envía a esta dirección.
customer_tax_idstringNoNúmero de identificación fiscal del cliente (NPWP en Indonesia, hasta 40 caracteres). Se imprime bajo la dirección del cliente en la factura.
itemsarraySíAl menos 1 ítem válido con descripción, quantity > 0 y price >= 0.
items[].descriptionstringNoNombre del producto o servicio. Si está vacío, se usa Item.
items[].quantitynumberSíNúmero de unidades, con hasta 2 decimales (por ejemplo 1.5 horas), de 0.01 a 999.999.
items[].pricenumberSíPrecio por unidad.
items[].discountnumberNoDescuento por ítem. Por defecto 0.
currencystringNoCódigo de moneda como EUR, USD o IDR.
tax_ratenumberNoPorcentaje de impuesto calculado sobre el subtotal tras descuentos. Por defecto 0.
due_datestringNoFormato YYYY-MM-DD. Por defecto 14 días después de la generación.
notesstringNoNotas adicionales visibles en la factura.
items[].unitstringNoEtiqueta de unidad junto a la cantidad, por ejemplo pcs u hora.
items[].tax_ratenumberNoPorcentaje de impuesto específico del ítem (0-100). Si se omite, usa el tax_rate de la factura.
discount_totalnumberNoDescuento a nivel de factura. Se resta antes del impuesto, por lo que el impuesto se calcula sobre el importe con descuento (repartido entre las líneas en proporción a su valor). Por defecto 0.
templatestringNoID de la plantilla de factura que se usará. Consulta las disponibles con GET /api/invoices/external/templates. Si se omite, se usa la plantilla predeterminada elegida en Ajustes y, después, la de Invosend.
document_languagestringNoIdioma de las palabras del documento de la factura (PDF y correo): en (predeterminado), id, es, fr, de, nl, it, pt, zh, ja, hi, ar (indonesio, español, francés, alemán, neerlandés, italiano, portugués de Brasil, chino simplificado, japonés, hindi, árabe). Cualquier otro valor se rechaza con un error. El diseño integrado sencillo, que solo se usa cuando no existe ninguna plantilla, siempre está en inglés.
custom_payment_notesstringNoInstrucciones de pago adicionales mostradas en el PDF.
footer_textstringNoTexto breve mostrado al pie del PDF.
is_recurringbooleanNoCrea una serie recurrente. Esta factura es la primera; las siguientes se generan y se envían automáticamente a customer_email según el calendario, y cada una usa 1 crédito de factura. Requiere un plan con la función de facturas recurrentes, además de recurring_interval y customer_email.
recurring_intervalstringNoCon qué frecuencia se repite la serie: uno de weekly, fortnightly, monthly, quarterly, semi-annually, yearly. Obligatorio cuando is_recurring es true.
recurring_countintegerNoNúmero total de facturas de la serie, incluida esta primera: un entero de 2 a 121. Por defecto 4. Usa este campo o recurring_end_date, no ambos.
recurring_end_datestringNoFecha a partir de la cual no se generan más facturas, YYYY-MM-DD, en el futuro. Usa este campo o recurring_count, no ambos.
payment_gatewaystringNoUno de midtrans, xendit, stripe, paypal, razorpay, mollie. La pasarela debe estar ya conectada en el workspace.
payment_accountstringNoID de una cuenta de pago existente (datos bancarios) para adjuntar a la factura.

Ejemplo completo de request:

curl -X POST https://app.invosend.com/api/invoices/generate-external \
  -H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Acme Corp",
    "customer_email": "billing@acme.com",
    "items": [
      { "description": "Consulting Services", "quantity": 2, "price": 500, "tax_rate": 11 }
    ],
    "discount_total": 50,
    "currency": "EUR",
    "notes": "Thank you for your business."
  }'

Ejemplo de factura recurrente (la primera ahora y luego una cada mes, 6 en total):

curl -X POST https://app.invosend.com/api/invoices/generate-external \
  -H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_name": "Acme Corp",
    "customer_email": "billing@acme.com",
    "items": [
      { "description": "Monthly retainer", "quantity": 1, "price": 2500 }
    ],
    "currency": "EUR",
    "recurring_interval": "monthly",
    "recurring_count": 6
  }'
Para reintentar con seguridad tras un tiempo de espera o un error de red, añada una cabecera Idempotency-Key (1-255 caracteres, por ejemplo un UUID que genere para cada factura). Si la primera solicitud tuvo éxito, el reintento devuelve la misma respuesta con la cabecera Idempotent-Replayed: true, sin crear otra factura ni usar otro crédito (credits_remaining es el valor de la primera llamada). Las claves se conservan 24 horas por espacio de trabajo. Solo se guardan las respuestas correctas, por lo que una solicitud fallida puede reintentarse con la misma clave.

4. Response#

200 OK significa que la factura fue creada y el PDF generado:

{
  "data": {
    "id": "cjld2cjxh0000qzrmn831i7rn",
    "invoice_number": "INV-1-202609-0016",
    "pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202609_0016_UNPAID_0da2beeabb.pdf",
    "credits_remaining": 24,
    "is_recurring": false,
    "template": {
      "id": "k3p9x2m7c0000abcd1234efgh",
      "name": "Classic",
      "source": "system_default"
    }
  }
}
credits_remaining es null si el workspace tiene cuota ilimitada. id se usa para llamar a los Endpoints adicionales de abajo. Las facturas creadas por API también aparecen en el listado del workspace y usan la misma secuencia de numeración. Los números de factura tienen la forma INV-{id del workspace}-{YYYYMM}-{secuencia}. En una factura recurrente, la respuesta incluye además un objeto recurring: schedule_id, interval, occurrences, next_issue_date, end_date. recurring.schedule_id es el id que se usa con los endpoints de recurrentes de más abajo.

5. Códigos de error#

StatusCausa
400Faltan campos obligatorios o son inválidos: customer_name, items vacío, ningún ítem válido, un payment_gateway no reconocido, un payment_gateway que no está conectado en este workspace o, en una factura recurrente, falta recurring_interval o customer_email, o recurring_count / recurring_end_date no son válidos. También se devuelve si no se pudo generar el PDF: repite la solicitud (no se descuenta ningún crédito).
402El balance invoice_credits del workspace está vacío.
403El header Authorization falta o tiene formato incorrecto, la API key es inválida/revocada o el plan no incluye acceso API.
403 (recurrente)Se envió is_recurring pero el plan del workspace no incluye facturación recurrente.
409Una solicitud con la misma Idempotency-Key aún se está procesando. Reintente en un momento.
422La Idempotency-Key ya se usó con un cuerpo de solicitud distinto. Use una clave nueva para una factura nueva.
429Rate limit excedido. Revisa la sección Rate Limit.

6. Rate Limit#

AlcanceLímite
Por workspace (todas las solicitudes a la API en conjunto)Professional 10/min · Specialist 20/min · Business 30/min · Enterprise 60/min
Por dirección IP (todas las solicitudes a la API en conjunto)30 requests/minuto
Claves API activas por workspaceProfessional 3 · Specialist 5 · Business 10 · Enterprise 20
Generar facturas y los endpoints de lectura/gestión comparten el mismo límite. Las respuestas correctas incluyen los encabezados X-RateLimit-Limit y X-RateLimit-Remaining. Las cuotas de API del workspace se configuran por plan de membresía en Invosend. ¿Necesitas más volumen en producción? Contáctanos desde tu cuenta de workspace. Las claves revocadas no cuentan para el límite de claves.

7. Endpoints adicionales#

La misma API key también puede leer y gestionar las facturas que creó mediante generate-external:

MétodoRutaDescripción
GET/api/invoices/externalLista las facturas de este workspace. Admite ?status= y ?page=/?pageSize=.
GET/api/invoices/external/templatesLista las plantillas de factura que puedes indicar en template, si tu plan permite cada una y cuál es la predeterminada.
GET/api/invoices/external/:idObtiene una factura por su id (el valor devuelto por generate-external).
PUT/api/invoices/external/:id/statusActualiza el estado de la factura. Body: { "status": "paid" } (uno de unpaid, paid, overdue, cancelled).
PUT/api/invoices/external/:id/archiveArchiva o desarchiva una factura. Body: { "is_archived": true }.
POST/api/invoices/external/:id/pdfVuelve a obtener la URL del PDF, regenerándolo si hace falta. Body: { "force": true } para forzar la regeneración.
GET/api/invoices/external/recurringLista las series recurrentes. Admite ?status= (active, paused, completed) y ?page=/?pageSize=.
GET/api/invoices/external/recurring/:idObtiene una serie (el recurring.schedule_id devuelto por generate-external) con todas sus repeticiones: fecha, estado y el id de la factura que generó cada una.
PUT/api/invoices/external/recurring/:id/statusPausa o reanuda una serie. Body: { "status": "paused" } o { "status": "active" }. Reanudar requiere un plan con facturas recurrentes.
PUT/api/invoices/external/recurring/:id/extendAñade repeticiones después de la última. Body: { "count": 3 } o { "end_date": "2027-12-31" }.
PUT/api/invoices/external/recurring/:id/frequencyCambia la frecuencia de la serie; las repeticiones futuras aún no generadas se reemplazan. Body: { "frequency": "monthly", "start_date": "2027-01-01", "count": 6 } (puede usar end_date en lugar de count; start_date debe ser futura).
POST/api/invoices/external/recurring/:id/skipElimina una repetición futura. Body: { "date": "2027-01-01" }. No se pueden eliminar las repeticiones que ya generaron una factura.
Para cobrar en línea, establece payment_gateway (y opcionalmente payment_account) en generate-external y luego envía a tu cliente la página de pago pública de la factura: https://app.invosend.com/p/invoice/{id}, usando el id que devuelve la API. Allí paga con la pasarela conectada y la factura se marca como pagada automáticamente. El pago lo gestiona esa página; no hay un endpoint de pago público aparte al que llamar.

8. Webhooks#

En lugar de consultar periódicamente, registre una URL HTTPS y Invosend le enviará una solicitud POST con un cuerpo JSON cada vez que cambie una factura. Añada endpoints en Ajustes > Webhooks (propietario del espacio de trabajo, plan con acceso a la API, hasta 5 endpoints). Cada endpoint tiene su propio secreto de firma, que se muestra una sola vez.

Los eventos se envían tanto para facturas creadas en el panel como a través de esta API. Las plantillas de series recurrentes nunca se envían.

EventoSe envía cuando
invoice.createdSe crea una factura. Es posible que el PDF aún no esté listo, por lo que pdf_url puede ser null.
invoice.paidEl estado cambia a paid (por usted, mediante la API o por un pago en línea).
invoice.overdueEl estado cambia a overdue (automáticamente tras el vencimiento, o manualmente).
invoice.cancelledEl estado cambia a cancelled.

Todos los eventos usan el mismo envoltorio. data.object es la factura, con la misma forma que GET /api/invoices/external/:id; los eventos de estado incluyen además data.previous_status:

{
  "id": "evt_9f2c1b7a4e3d5c6b8a1f0e2d",
  "object": "event",
  "type": "invoice.paid",
  "created": "2026-10-01T09:30:12.000Z",
  "data": {
    "object": {
      "id": "cjld2cjxh0000qzrmn831i7rn",
      "invoice_number": "INV-1-202610-0007",
      "customer_name": "Acme Corp",
      "customer_email": "billing@acme.com",
      "currency": "USD",
      "subtotal": 500,
      "discount_total": 0,
      "tax_rate": 0,
      "tax_amount": 0,
      "amount": 500,
      "amount_paid": 500,
      "amount_credited": 0,
      "amount_received": 500,
      "invoice_status": "paid",
      "is_archived": false,
      "is_recurring": false,
      "recurring_interval": "none",
      "issue_date": "2026-10-01T02:15:40.000Z",
      "due_date": "2026-10-15",
      "paid_at": "2026-10-01T09:30:11.000Z",
      "pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202610_0007_PAID.pdf",
      "pdf_status": "ready"
    },
    "previous_status": "unpaid"
  }
}

Cada solicitud incluye estas cabeceras. Para verificarla, calcule un HMAC-SHA256 de {t}.{cuerpo sin procesar} con el secreto del endpoint y compárelo con v1; rechácela si t tiene más de 5 minutos:

POST /your/webhook/url
Content-Type: application/json
Invosend-Event: invoice.paid
Invosend-Delivery: evt_9f2c1b7a4e3d5c6b8a1f0e2d
Invosend-Signature: t=1790847012,v1=5b1d...e9a2

// Node.js
import crypto from "crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(parts.t + "." + rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
Responda con cualquier código 2xx en menos de 10 segundos. En caso contrario se reintenta hasta 7 veces más (tras 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas, 12 horas y 24 horas). Un endpoint que sigue fallando se desactiva y se le avisa. Un evento puede llegar más de una vez o en desorden: use el id del evento para ignorar duplicados y trate invoice_status como la fuente de verdad. Los registros de entrega se conservan 30 días y cualquier evento puede reenviarse desde Ajustes > Webhooks.

9. Limitaciones#

Esta API está pensada solo para generar facturas y una gestión ligera. Lo siguiente aún no está disponible:

  • Los webhooks solo cubren factura creada, pagada, vencida y cancelada. Otros cambios, como ediciones o archivado, no se envían: consulte el estado actual con GET /invoices/external/:id.
  • La factura creada en cada solicitud no se envía por correo: esta API solo genera el PDF, así que hacérselo llegar al cliente depende de ti (o usa el panel). La excepción son las series recurrentes: cada factura posterior generada por el programador se envía automáticamente a customer_email.
  • No se puede editar una factura después de creada — solo su estado y su estado de archivado se pueden cambiar por esta API.
  • El soporte de pasarelas de pago requiere que la pasarela ya esté conectada en el dashboard; esta API solo la asocia a la factura, no la conecta por sí sola.
  • Una serie recurrente puede pausarse, reanudarse, ampliarse, reprogramarse y omitir repeticiones futuras mediante la API, pero su contenido (artículos, cliente, importes) no se puede editar ni la serie eliminar desde la API; hágalo en el panel.

Preguntas frecuentes#

¿Cómo obtengo una API key de Invosend?

Crea una cuenta en app.invosend.com, actualiza el workspace a un plan de pago, abre Settings > API Keys y pulsa Generate key. La clave original se muestra una sola vez al crearla.

¿Cuánto cuesta cada llamada de generación?

Cada solicitud exitosa descuenta 1 crédito de factura del balance del workspace. Usa el mismo pool de créditos que las facturas creadas desde el dashboard.

¿Cuál es el rate limit de esta API?

El límite de tasa del workspace depende de la configuración de tu plan de pago. La sección Rate Limit de arriba muestra el límite actual de cada plan.