Recipe: invoice
Goal: your billing service generates a branded PDF invoice for every order, while the layout stays editable by anyone in the editor.
You’ll use: text boxes, a header band, a logo, required and optional variables, the export API.
1. Design the template
Section titled “1. Design the template”-
Create a template named
Invoice. Its title becomes the PDF file name (Invoice.pdf). -
Header band — draw a rectangle across the top of the page (about 1920 × 180), fill it with your brand color, stroke width
0, then Send to back. -
Logo — click the image tool and upload your logo as PNG; move it onto the band.
-
Title — a text box with
INVOICEin Courier Prime or Anton, 64 px, white, on the band. -
Variables — create the following, inserting each one in a text box next to its label:
Variable Required Default invoiceNumber✅ issueDate✅ customerName✅ customerAddressdescription✅ amount✅ vat0.00total✅ paymentTermsPayment due within 30 days. -
Separators — thin rectangles (height 2) make clean lines between sections.
Click Imprime and fill in the form to check the result. Once it looks right, note the template ID from the URL.
2. Generate it from your code
Section titled “2. Generate it from your code”Format every value in your code — Imprime inserts strings as they are.
const IMPRIME_URL = 'https://app.imprime.io';const INVOICE_TEMPLATE_ID = process.env.IMPRIME_INVOICE_TEMPLATE_ID!;
const eur = new Intl.NumberFormat('en-IE', { style: 'currency', currency: 'EUR' });const date = new Intl.DateTimeFormat('en-GB', { dateStyle: 'long' });
export async function renderInvoice(order: Order): Promise<Buffer> { const res = await fetch(`${IMPRIME_URL}/api/export/${INVOICE_TEMPLATE_ID}/pdf`, { method: 'POST', headers: { 'x-api-key': process.env.IMPRIME_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ invoiceNumber: order.invoiceNumber, issueDate: date.format(order.createdAt), customerName: order.customer.name, customerAddress: order.customer.address, description: order.description, amount: eur.format(order.amount), vat: eur.format(order.vat), total: eur.format(order.amount + order.vat), }), });
if (!res.ok) { const { error } = await res.json(); throw new Error(`Invoice generation failed (${res.status}): ${error}`); } return Buffer.from(await res.arrayBuffer());}import osimport requests
IMPRIME_URL = "https://app.imprime.io"TEMPLATE_ID = os.environ["IMPRIME_INVOICE_TEMPLATE_ID"]
def render_invoice(order) -> bytes: res = requests.post( f"{IMPRIME_URL}/api/export/{TEMPLATE_ID}/pdf", headers={"x-api-key": os.environ["IMPRIME_API_KEY"]}, json={ "invoiceNumber": order.invoice_number, "issueDate": order.created_at.strftime("%d %B %Y"), "customerName": order.customer.name, "customerAddress": order.customer.address, "description": order.description, "amount": f"€{order.amount:,.2f}", "vat": f"€{order.vat:,.2f}", "total": f"€{order.amount + order.vat:,.2f}", }, timeout=60, ) if not res.ok: raise RuntimeError(f"Invoice generation failed ({res.status_code}): {res.json()['error']}") return res.contentThen store the buffer (S3, database, disk) or attach it to the confirmation email.
3. Let the layout evolve
Section titled “3. Let the layout evolve”When marketing wants a new color or legal wants an extra sentence, they edit the template in Imprime. Your code doesn’t change, as long as variable names stay the same.