Credit note
Build a credit note that reverses an invoice you already issued. You learn how to reference the original, an overlay stamp using opacity, and how to receive the PDF bytes instead of a URL.
What this teaches
Building it in the editor
- Start by duplicating the invoice template — a credit note is the same table with the sign reversed.
- Change the title to Credit note and reference the original number below it. {{ upper(data.origin) }} normalises the case so it is easy to compare by eye.
- Add another label reading "VOID" in a large size and drop its opacity to 0.12. Overlaid on the body it does not obscure the text.
- Overlap order comes from the object tree. Bring the stamp to the front so it sits over the body.
- The amounts arrive negative. Currency formatting renders negatives by the rules of the locale, so there is nothing extra to do.
Building it with the schema and API
- If your accounting system needs the file directly, send export_type "file". The response body is the PDF itself with content-type application/pdf.
- "file" works only on POST /v1/create. Async and batch reject it, because there is no file yet at the moment we answer.
- Any other value is rejected rather than quietly defaulting — a silently ignored option hides your typo forever.
- String helpers such as upper, lower, trim, sub, replace, pad and split are available in expressions. replace takes literals only — no regular expressions.
The finished document
The JSON below is exactly the document those two routes produce. Our tests validate it and check that it renders without warnings, so you can paste it and use it as is.
{
"v": 1,
"unit": "px",
"page": {
"size": "a4",
"orientation": "portrait",
"margin": {
"t": 48,
"r": 48,
"b": 56,
"l": 48
},
"header": "",
"footer": "{{ data.company }} · [[page]] / [[pages]]"
},
"fonts": [],
"styles": [
{
"id": "s_h1",
"fontFamily": "Liberation Sans",
"fontSize": 26,
"fontWeight": 700,
"italic": false,
"color": "#111111",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_l",
"fontFamily": "Liberation Sans",
"fontSize": 13,
"fontWeight": 400,
"italic": false,
"color": "#333333",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_tbl",
"fontFamily": "Liberation Sans",
"fontSize": 12,
"fontWeight": 400,
"italic": false,
"color": "#111111",
"bg": "transparent",
"align": "left",
"valign": "middle",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_total",
"fontFamily": "Liberation Sans",
"fontSize": 18,
"fontWeight": 700,
"italic": false,
"color": "#111111",
"bg": "transparent",
"align": "right",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_void",
"fontFamily": "Liberation Sans",
"fontSize": 96,
"fontWeight": 700,
"italic": false,
"color": "#d92d20",
"bg": "transparent",
"align": "center",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 0.12
}
],
"nodes": [
{
"id": "n_title",
"parent": "",
"order": 0,
"type": "label",
"x": 0,
"y": 0,
"w": 460,
"h": 34,
"show": "",
"styleId": "s_h1",
"props": {
"text": "Credit note",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Title"
},
{
"id": "n_origin",
"parent": "",
"order": 1,
"type": "label",
"x": 0,
"y": 42,
"w": 460,
"h": 20,
"show": "",
"styleId": "s_l",
"props": {
"text": "Refund against invoice {{ upper(data.origin) }}",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Original"
},
{
"id": "n_items",
"parent": "",
"order": 2,
"type": "table",
"x": 0,
"y": 96,
"w": 697,
"h": 130,
"show": "",
"styleId": "s_tbl",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#c9ced6",
"fill": "#f3f5f7",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 6,
"padX": 8,
"align": "",
"valign": "middle",
"wrap": "",
"bind": "data.items",
"headerHeight": 28,
"rowHeight": 24,
"max": 100,
"columns": [
{
"header": "Refunded item",
"cell": "{{ item.name }}",
"width": 497,
"align": "left"
},
{
"header": "Amount",
"cell": "{{ format(item.amount, \"currency\", \"USD\", \"en-US\") }}",
"width": 200,
"align": "right"
}
],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Refunded items"
},
{
"id": "n_total",
"parent": "",
"order": 3,
"type": "label",
"x": 0,
"y": 240,
"w": 697,
"h": 26,
"show": "",
"styleId": "s_total",
"props": {
"text": "Refund total {{ format(sum(data.items, \"amount\"), \"currency\", \"USD\", \"en-US\") }}",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Total"
},
{
"id": "n_void",
"parent": "",
"order": 4,
"type": "label",
"x": 0,
"y": 300,
"w": 697,
"h": 120,
"show": "",
"styleId": "s_void",
"props": {
"text": "VOID",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Void stamp"
}
]
}The request body. Put the document above into template and the data below into data.
{
"template": "(the document above)",
"data": {
"company": "brewmypdf",
"origin": "inv-2026-0042",
"items": [
{
"name": "Maintenance (3 months) refund",
"amount": -840
},
{
"name": "Over-billing adjustment",
"amount": -120
}
]
},
"export_type": "file"
}