Developer Document API · Beta

Generate invoice PDFs and EN16931-core e-invoice XML with the InvoiceCraftly Developer Document API

Send a JSON invoice to one authenticated endpoint and get back a real, print-ready invoice PDF — rendered with the same InvoiceCraftly template your customers already see in the browser editor. Two more endpoints check EN16931-core e-invoice readiness and return the structured XML itself. No headless browser to run yourself, no HTML-to-PDF pipeline, no separate e-invoice validator to maintain.

Beta · invoice PDF, readiness & EN16931-core XML only Base URL: https://invoicecraftly.com
Endpoints
POST /api/v1/documents/pdf
POST /api/v1/invoices/readiness
POST /api/v1/documents/structured
Auth
Authorization: Bearer dk_live_… — same key for all three endpoints
Request format
JSON invoice document, application/json
Response format
PDF endpoint: application/pdf. Readiness & structured endpoints: JSON, with EN16931-core XML embedded when ready.
Free monthly quota
100 PDF renders per API key. Readiness & structured endpoints aren't quota-limited yet.
Supported document
Invoice only. PDF endpoint renders the Ledger template; XML endpoints target the EN16931-core profile, not Peppol BIS.
Official TypeScript client · npm

Prefer a typed client instead of raw HTTP?

@invoicecraftly/client is the official thin TypeScript client for the same released API documented below. It handles authentication, typed request/response shapes, PDF bytes, public API errors, timeouts and cancellation while keeping InvoiceCraftly's server authoritative for calculations, readiness and structured output.

  • v0.1.0
  • Node 18+
  • 0 runtime dependencies
  • MIT
Install
npm install @invoicecraftly/client
Quick start
import { InvoiceCraftly } from '@invoicecraftly/client';

const client = new InvoiceCraftly({
  apiKey: process.env.INVOICECRAFTLY_API_KEY
});

const pdf = await client.documents.pdf(invoice);

Keep the API key server-side. The package has no telemetry and does not implement invoice/VAT/routing/compliance rules locally.

Official Python client · PyPI

Using Python?

invoicecraftly is the official thin Python client for the same released API. It handles bearer authentication, typed request/response shapes, PDF bytes, public API errors and timeouts while keeping InvoiceCraftly's server authoritative for invoice calculations, readiness and structured output.

  • v0.1.1
  • Python 3.11+
  • 0 runtime dependencies
  • MIT
Install
pip install invoicecraftly
Quick start
import os
from invoicecraftly import InvoiceCraftly

client = InvoiceCraftly(
  api_key=os.environ["INVOICECRAFTLY_API_KEY"]
)

pdf = client.documents.pdf(invoice)

Keep the API key server-side. The package has no telemetry and does not implement invoice/VAT/routing/compliance rules locally.

Framework example · Next.js

Prefer a complete Next.js starting point?

Clone a small App Router example that keeps INVOICECRAFTLY_API_KEY inside a server Route Handler, submits invoice fields from the browser to the local app, calls the official TypeScript client on the server, and returns the generated PDF for download.

  • Next.js 16.3.4
  • Node 20.9+
  • SDK 0.1.0
  • MIT
Quick start
git clone https://github.com/abaidurrehman/nextjs-invoicecraftly-example.git
cd nextjs-invoicecraftly-example
npm ci
# add INVOICECRAFTLY_API_KEY to .env.local
npm run dev

Keep INVOICECRAFTLY_API_KEY server-side and never rename it to NEXT_PUBLIC_INVOICECRAFTLY_API_KEY. This is a runnable source example, not a Next.js partnership or a claim that InvoiceCraftly hosts a live demo.

1. Get an API key

Sign in with your InvoiceCraftly account, then generate a key from the account page. Only one key is active per account — generating a new key immediately revokes the previous one. The full key is shown exactly once, at generation time, so store it somewhere safe.

Sign in and get your API key

2. Authenticate your request

Every request to the Developer Document API carries your key as a bearer token in the Authorization header. Keys always start with the dk_live_ prefix, which makes a leaked key easy to spot and rotate.

Authorization: Bearer dk_live_YOUR_API_KEY_HERE
Content-Type: application/json

A missing, malformed, or revoked key returns 401 with error code AUTHENTICATION_FAILED. Query-string keys are never accepted — the key must be sent in the Authorization header.

3. Send an invoice, get a PDF back

POST/api/v1/documents/pdf

The request body is a single JSON invoice document — no wrapper object, no multi-step upload. Save this fixture as invoice.json in your project; every example below sends this exact document:

{
  "type": "invoice",
  "number": "INV-1042",
  "issueDate": "2026-09-05",
  "dueDate": "2026-09-19",
  "currency": "USD",
  "seller": {
    "name": "Fixture Seller Studio",
    "addressLines": ["12 Render Way", "Austin, TX 73301", "United States"]
  },
  "buyer": {
    "name": "Fixture Buyer Co",
    "addressLines": ["400 Client Ave", "Denver, CO 80202", "United States"]
  },
  "items": [
    {
      "description": "Brand design retainer",
      "quantity": 2,
      "unitPrice": 150,
      "taxRate": 8.25,
      "taxLabel": "Sales Tax"
    }
  ],
  "payment": {
    "iban": "DE89370400440532013000",
    "reference": "INV-1042",
    "terms": "Net 14"
  }
}
curl https://invoicecraftly.com/api/v1/documents/pdf \
  -X POST \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  --data @invoice.json \
  --output invoice.pdf

A successful call returns 200 with Content-Type: application/pdf and the finished document as the response body — write it straight to a file or stream it to your own user. An X-Render-Duration-Ms header reports how long the render took.

4. Check EN16931-core e-invoice readiness

POST/api/v1/invoices/readiness

Before you commit to generating a structured e-invoice, check whether an invoice is ready — and see exactly what's missing if it isn't. This endpoint returns diagnostics only, no XML. The body is the same document object as the PDF endpoint, plus an optional supplement object carrying EN16931-core-specific fields (postal addresses, tax identifiers, per-line unit and VAT category codes) that a plain invoice document doesn't otherwise capture:

{
  "document": { /* same shape as the PDF endpoint's invoice.json */ },
  "supplement": {
    "version": 1,
    "seller": {
      "postalAddress": { "countryCode": "NO", "city": "Oslo", "postalCode": "0154" },
      "taxId": { "role": "legal-registration", "schemeId": "0192" },
      "vatIdentifier": "NO123456785MVA"
    },
    "buyer": {
      "postalAddress": { "countryCode": "NO", "city": "Oslo", "postalCode": "0184" }
    },
    "lines": [
      { "sourceIndex": 0, "unitCode": "HUR", "vatCategoryCode": "S" }
    ]
  }
}
curl https://invoicecraftly.com/api/v1/invoices/readiness \
  -X POST \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  --data @readiness-request.json

A successful call returns 200 with a JSON body — never a 422 for an invoice that's merely incomplete, since "not ready yet" is a normal, expected outcome here:

{
  "apiVersion": "v1",
  "profileId": "en16931-core",
  "specificationIdentifier": "urn:cen.eu:en16931:2017",
  "ready": false,
  "gaps": [
    { "id": "EN16931-CORE-SELLER-VAT-IDENTIFIER", "message": "Seller is missing a VAT identifier." }
  ]
}

There's no artifact or xml field in this response — this endpoint reports diagnostics only. Once gaps is empty and ready is true, call the structured artifact endpoint below to get the actual XML.

5. Get the EN16931-core XML artifact

POST/api/v1/documents/structured

Same request body as the readiness endpoint above — document plus optional supplement — but the response also carries the rendered EN16931-core UBL/XML when the invoice is ready:

curl https://invoicecraftly.com/api/v1/documents/structured \
  -X POST \
  -H "Authorization: Bearer dk_live_YOUR_API_KEY_HERE" \
  -H "Content-Type: application/json" \
  --data @readiness-request.json

A successful call returns 200 with JSON. When ready, artifact.content holds the EN16931-core XML as a string; when not ready, artifact is null and gaps explains why:

{
  "apiVersion": "v1",
  "profileId": "en16931-core",
  "specificationIdentifier": "urn:cen.eu:en16931:2017",
  "ready": true,
  "gaps": [],
  "artifact": { "mediaType": "application/xml", "content": "<Invoice ...>...</Invoice>" }
}

This is EN16931-core UBL/XML, not a Peppol BIS artifact. There's no network delivery, no Access Point transmission, and no official-conformance claim — it's a structured export you can feed into your own validation or delivery pipeline, not a finished Peppol e-invoice.

Request reference

Invoice document fields

The request body is a PublicDocumentV1 invoice: plain, versioned JSON — not InvoiceCraftly's internal editor format. Unknown fields are rejected outright rather than silently ignored.

Top-level and nested invoice fields
FieldTypeRequiredNotes
typestringYesMust be "invoice" — other document types are recognized but not yet rendered (TARGET_NOT_SUPPORTED).
numberstringNoYour own invoice number.
issueDatestringYesISO date, YYYY-MM-DD.
dueDatestringNoISO date, YYYY-MM-DD.
currencystringYes3-letter uppercase currency code, e.g. USD, EUR, DKK.
localestringNoBCP 47 locale for number/date formatting, e.g. da-DK.
templatestringNoMust be "ledger" if present — the only template this endpoint renders today.
seller.name / buyer.namestringYesNon-empty business or individual name.
seller.addressLines / buyer.addressLinesstring[]YesEach line rendered as written.
items[]arrayYesAt least one line item.
items[].descriptionstringYesLine item description.
items[].quantitynumberYesGreater than 0.
items[].unitPricenumberYes0 or greater.
items[].taxRatenumberYes0–100.
items[].taxLabelstringNoe.g. "VAT", "Sales Tax", "Moms".
paymentobjectNobankName, iban, bic, accountNumber, routingNumber, reference, poNumber, terms, dueText, qrMode.
notesstringNoFree-text note printed on the document.
brandingobjectNoaccentColor (hex), stampStyle (round/square/none), logo (data base64 + mimeType, PNG/JPEG, 512 KB decoded max).

The readiness and structured artifact endpoints wrap this same document in a request envelope, plus an optional supplement object carrying EN16931-core-specific fields a plain invoice document doesn't capture:

Compliance supplement fields (readiness & structured endpoints only)
FieldTypeRequiredNotes
versionnumberNoMust be 1 if present.
buyerReferencestringNoBuyer's own reference/order code.
seller.postalAddress / buyer.postalAddressobjectNocountryCode (ISO 3166-1 alpha-2), city, postalCode, region.
seller.taxIdobjectNorole (seller-id / legal-registration / tax-registration / vat), schemeId, taxSchemeId.
seller.vatIdentifier / buyer.vatIdentifierstringNoVAT identifier, e.g. NO123456785MVA.
lines[].sourceIndexnumberYes, within each line entry0-based index matching a position in the document.items array.
lines[].unitCodestringNoUN/CEFACT unit code, e.g. HUR, C62.
lines[].vatCategoryCodestringNoOne of AE, B, E, G, K, L, M, O, S, Z.

Missing supplement fields aren't rejected — they simply show up as entries in the response's gaps array, so you can call the readiness endpoint first, fill in what it flags, then re-check.

Errors

Every error — validation, authentication, quota, or render failure — comes back as the same JSON envelope, never a raw stack trace or an undocumented code:

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests. Please slow down.",
    "requestId": null,
    "details": []
  }
}
Error codes
HTTP statusCodeMeaning
400 / 413 / 415INVALID_REQUESTMalformed JSON, unknown field, oversized body, or wrong content type.
401AUTHENTICATION_FAILEDMissing, malformed, or revoked API key.
422TARGET_NOT_SUPPORTEDStructurally valid document, but the requested type or template isn't rendered yet.
429RATE_LIMITEDMonthly quota reached on the PDF endpoint; a Retry-After header reports seconds until reset. The readiness and structured endpoints don't quota-limit yet.
503 / 504RENDER_TIMEOUT / PDF_RENDER_FAILEDPDF endpoint only — the render didn't finish in time, or failed on InvoiceCraftly's side. Safe to retry.
Version history

API changelog

This tracks changes to the /v1 contract itself — request/response shape, authentication, and quota behaviour. For product-wide feature announcements across the whole InvoiceCraftly app, see the changelog.

  • 2026-09-06 — Added POST /api/v1/invoices/readiness (EN16931-core e-invoice readiness diagnostics, no artifact) and POST /api/v1/documents/structured (readiness diagnostics plus the EN16931-core UBL/XML artifact when ready). Both reuse the same API key as the PDF endpoint, aren't quota-limited yet, and never claim Peppol BIS conformance.
  • 2026-09-05v1 beta launched: POST /api/v1/documents/pdf, invoice document type with the Ledger template only, API keys with a 100-render monthly quota.
Current limits

Current beta scope

All three endpoints handle one document type (invoice) only, and a request body capped around 900 KB (roughly a 512 KB logo once decoded, plus the rest of the invoice). The PDF endpoint renders one layout (the Ledger template) and is billed against your account's 100-render monthly quota regardless of success or failure once the render actually starts; the readiness and structured artifact endpoints aren't quota-limited yet. All three process your invoice payload on InvoiceCraftly's server — that's different from the browser editor, which keeps documents on your own device. Read the engineering notes on that data boundary and the privacy policy for the current retention behaviour. The structured XML endpoint targets the EN16931-core profile only — no Peppol BIS artifact, no Access Point delivery, and no official-conformance claim. There's no quote/estimate/receipt/credit-note rendering yet, and no billing beyond the free monthly quota.

Frequently asked questions

Does the API support anything besides invoices?

Not yet — every endpoint works with one document type (invoice) only, and the PDF endpoint renders the Ledger template only. See the current limits for what's not supported.

Can I use my own invoice template with the API?

Not yet. The PDF endpoint renders the same Ledger template available in the browser editor — there's no per-request template choice today.

Is there a free tier?

Yes — 100 PDF renders per month per API key during the beta, with no billing yet. The readiness and structured XML endpoints share the same key but aren't quota-limited yet.

Do I need a backend to use this, or can I call it from the browser?

A backend. Your API key is a secret and must never be embedded in client-side JavaScript that ships to a browser.

What happens if I exceed my quota?

You get back 429 with error code RATE_LIMITED and a Retry-After header. See the error table for the full list of codes.

Does the structured XML endpoint produce Peppol-conformant e-invoices?

No. It produces EN16931-core UBL/XML only — no Peppol BIS artifact, network delivery, or official-conformance claim. Treat it as a starting point, not a final compliance decision.

What's the difference between the readiness endpoint and the structured artifact endpoint?

POST /api/v1/invoices/readiness returns diagnostics only — whether the invoice is ready and what's missing. POST /api/v1/documents/structured returns the same diagnostics plus the EN16931-core XML itself once the invoice is ready.

Start building

Get an API key and generate your first invoice PDF.

Free during the beta, 100 renders included per month.

Get your API key