PDF Invoice Generation API
Introduction
Welcome to the JSON 2 Invoice API! This service allows you to dynamically create professional PDF invoices from JSON data. It offers a range of customization options, including language settings, currency formatting, company logos, QR codes, and precise invoice total rounding, making it adaptable to diverse business needs.
Endpoint & Method
Endpoint URL: https://api.json2invoice.com
Method: POST
Authentication
The API secures requests using an API Key. This key must be included in the HTTP headers of your request:
| Property | Description | Type | Default |
|---|---|---|---|
| Header Name | The name of the HTTP header used to transmit the API key. | X-API-Key | - |
| Value | Your unique client API key provided to you (e.g., YOUR_CLIENT_API_KEY_HERE). | string | Your API Key |
If the API Key is missing, invalid, or not authorized for the operation, the API will respond with a 403 Forbidden error. Your API keys are also linked to a customerId on our backend for service usage and billing, which is managed by our API authorization layer.
Request Body Structure
All requests to the API must send a JSON object as the request body. This JSON object has two primary top-level keys:
data(Required): An object containing all the invoice definitions, content, and customization settings. This is the core of your invoice request. See Core Payload: The `data` Object for details.lang(Optional): An object allowing you to override specific translation strings for the current request. See Feature: Translations & Text for details.
Content-Type Header: Ensure your request includes the header Content-Type: application/json.
Example Top-Level Structure:
{
"data": {
"language": "en",
"timezone": "America/New_York",
"from": { /* Sender Address Object */ },
"to": { /* Recipient Address Object */ },
"ship_to": { /* Optional Shipping/Delivery Address Object */ },
"invoice": { /* Invoice Details Object */ }
// ... other fields as described in "Core data Object Fields"
},
"lang": { // Optional: For overriding specific text elements
"due_date": "Payment Due By",
"item_type": "Service Category"
}
}Core Payload: The data Object
The data object is where you define the specifics of your invoice. The following fields are available directly within the data object:
| Property | Description | Type | Default |
|---|---|---|---|
| from | Sender's address details (optional). The JSON key is from. If provided, the name field within is required. See Address Object. | Address Object | Optional |
| to | Recipient's address details (optional). If provided, the name field within is required. See Address Object. | Address Object | Optional |
| ship_to | Shipping/delivery address (optional), rendered below the recipient (to) block. Useful for billed-to / shipped-to scenarios. If provided, the name field within is required. See Address Object. | Address Object | Optional |
| document_title | Document heading rendered above the details table (e.g., "Lieferschein", "Delivery Note"). Max 100 characters. See Delivery Notes & Custom Docs. | string | Optional |
| hide_amounts | If true, hides the unit price and amount columns plus the entire totals summary — for delivery notes and other price-free documents. See Delivery Notes & Custom Docs. | boolean | false |
| items_title | Heading rendered directly above the items table (e.g., "In this order"). Max 100 characters. Fully customizable; omit for no heading. See Delivery Notes & Custom Docs. | string | Optional |
| item_checkboxes | If true, renders an empty checkbox at the start of each item row so staff can tick off articles as they pack or verify a box. See Delivery Notes & Custom Docs. | boolean | false |
| invoice | Main invoice details. See Invoice Object. | Invoice Object | Required |
| language | Two-letter language code for base translations. See Translations & Text. | string | "en" |
| babel_locale | Specific Babel locale (e.g., "en_US", "de_DE") for date and currency formatting. Overrides locale derived from language or currency. | string | Optional |
| timezone | Timezone identifier (e.g., "America/New_York", "Europe/Berlin") for date formatting. See Dates & Timestamps. | string | "UTC" |
| decimal_separator | Character to use as the decimal separator for fallback currency formatting. See Numeric & Currency. | string | "." |
| thousands_separator | Character to use as the thousands separator for fallback currency formatting. See Numeric & Currency. | string | "," |
| use_int_for_currency | If true, all monetary values (e.g., unit_price, total, discount_amount) in the invoice object are treated as integers representing the smallest currency unit (e.g., cents for USD, $123.45 becomes 12345). See Numeric & Currency. | boolean | false |
| qr_position | Placement of the QR code on the PDF (if qr_link is provided). See QR Codes. | "top" | "bottom" | "bottom" |
| text_logo | Text to display as a logo if an image logo (logo_link) is not provided or fails to load. See Logos & Branding. | string | Optional |
| qr_link | A valid URL to be encoded into a QR code on the invoice. See QR Codes. | URL string | Optional |
| qr_desc | A short description or call to action displayed below the QR code. See QR Codes. | string | Optional |
| logo_link | A publicly accessible URL of an image (PNG, JPG) or SVG file to be used as the company logo. See Logos & Branding. | URL string | Optional |
| logo_max_height | Maximum height in pixels for the logo. The logo will be scaled down to fit if it exceeds this height, maintaining aspect ratio. See Logos & Branding. | integer | 60 (Optional) |
| logo_max_width | Maximum width in pixels for the logo. The logo will be scaled down to fit if it exceeds this width, maintaining aspect ratio. See Logos & Branding. | integer | 250 (Optional) |
| logo_min_height | Minimum height in pixels for the logo. This can influence rendering, especially for SVGs. See Logos & Branding. | integer | 40 (Optional) |
| watermark | Configuration for adding a text watermark to the invoice. See Watermark. | Watermark Object | Optional |
| ribbon | Configuration for adding a corner ribbon (e.g., "PAID", "DRAFT") to the invoice. See Corner Ribbon. | Ribbon Object | Optional |
| refund_display | Visual configuration (colors, stripes, strikethrough) for refunded items and refund summary rows. See Refunds. | RefundDisplay Object | Optional |
| qr_bill | Renders a Swiss QR bill payment part (QR-Rechnung) in the PDF. See Swiss QR Bill. | QrBill Object | Optional |
Address Object (from, to, ship_to)
The Address Object is used to specify sender (data.from), recipient (data.to), and shipping/delivery (data.ship_to) details. All address objects are optional in the payload, but if you include one, the name field within that address object is required. The JSON key for the sender's address must be from.
The ship_to block is rendered below the recipient (to) block — ideal for invoices where the billing party and the delivery destination differ (billed-to / shipped-to). Combine it with the label property to fully customize the headings, e.g. "label": "Billed To" on to and "label": "Shipped To" on ship_to.
Example:
{
"name": "Example Corp LLC",
"street": "789 Business Park Ave",
"place": "Chicago, IL 60601",
"country": "United States",
"extra": "Floor 12, Attn: Finance Dept.",
"address_lines_order": ["name", "extra", "street", "place", "country"]
}Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| name | Full name or company name. | string | Required |
| street | Street address line (e.g., street name and number). | string | Optional |
| place | City, State/Province, and Postal Code (e.g., "New York, NY 10001"). | string | Optional |
| country | Country name. | string | Optional |
| extra | An additional address line for details like P.O. Box, suite number, or an attention line. | string | Optional |
| label | Custom heading for this address block on the PDF, overriding the default translated label (e.g., "Billed To" instead of "To", or "Spedito a" for a ship_to block). Max 50 characters. | string | Optional |
| address_lines_order | Specifies the display order of the address fields on the PDF. Valid fields are: "name", "street", "place", "country", "extra". | list of strings | ["name", "street", "place", |
Invoice Object (invoice)
The data.invoice object contains the core financial and descriptive details of the invoice itself.
Example Snippet:
{
"id": "INV-US-2024-101",
"currency": "USD",
"taxes_method": "excluded",
"date": "July 20, 2024",
"due_date_timestamp": 1724025600,
"date_format": "MMMM d, yy",
"subject": "Annual Maintenance Contract",
"items": [ /* Array of Item Objects */ ],
"taxes": [ /* Array of Tax Objects */ ],
"rounding_target_cents": 0,
"rounding_method": "closest"
}Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| id | Unique identifier for the invoice (e.g., "INV-2024-001"). | string | Required |
| currency | 3-letter ISO 4217 currency code (e.g., "USD", "EUR", "JPY"). | string | "USD" |
| items | An array of line items for the invoice. Each item must be an Item Object. | list of Item Objects | Required (min 1 item) |
| discount_amount | A fixed monetary discount applied to the subtotal before taxes. See Numeric & Currency for FlexibleDecimal details. | FlexibleDecimal | "0.00" |
| discount_percentage | A percentage discount (0-100) applied to the subtotal (after any fixed discount_amount) before taxes. E.g., "2.5" for 2.5%. See Numeric & Currency. | FlexibleDecimal | "0.00" |
| taxes_method | Determines how taxes are calculated: "excluded" (taxes are added to the subtotal after discounts), "included" (item prices already include taxes, which are then backed out for reporting). | "included" | "excluded" | "excluded" |
| taxes | An array defining up to 3 global tax types applicable to invoice items. See Tax Object. | list of Tax Objects | [] (Optional) |
| date_format | The format for displaying dates (invoice date, due date) if they are generated from timestamps. Uses Babel date format patterns. See Dates & Timestamps. | string | "d MMMM y" (Optional) |
| purchase_order | Purchase order number associated with the invoice. | string | Optional |
| date_timestamp | Invoice issue date as a Unix timestamp (seconds since epoch). If provided, date_format will be used. Superseded by date if both are present. See Dates & Timestamps. | integer | Optional |
| due_date_timestamp | Invoice due date as a Unix timestamp. If provided, date_format will be used. Superseded by due_date if both are present. See Dates & Timestamps. | integer | Optional |
| date | Pre-formatted invoice issue date string. If provided, this is used directly, ignoring date_timestamp and date_format for the invoice date. See Dates & Timestamps. | string | Optional |
| due_date | Pre-formatted invoice due date string. If provided, this is used directly, ignoring due_date_timestamp and date_format for the due date. See Dates & Timestamps. | string | Optional |
| subject | A subject or title for the invoice (e.g., "Consulting Services - Q3"). | string | Optional |
| notes | Additional notes, terms and conditions, or payment instructions to include on the invoice. | string | Optional |
| rounding_target_cents | Target cent value for rounding the final total. E.g., 5 rounds to the nearest .05. 0 rounds to the nearest whole currency unit. See Invoice Total Rounding. | Literal[0, 5, 10, 20, 50] | Optional (no rounding if omitted) |
| rounding_method | Method for rounding. closest: round to nearest standard. discount: round down (floor). penalty: round up (ceiling). Effective only if rounding_target_cents is provided. See Invoice Total Rounding. | Literal["closest", | "discount" |
| paid_amount | Amount already paid towards this invoice. When provided, displays "Amount Paid" and calculates "Balance Due" in the invoice summary. See Paid Amount. | FlexibleDecimal | Optional |
| custom_fields | Up to 10 additional {"label": "...", "value": "..."} rows rendered in the details header table after the standard rows — e.g. order date, dispatch date, references, contact person. Label max 50 chars, value max 200 chars. See Delivery Notes & Custom Docs. | list of objects | Optional |
| refunds | Up to 10 lump-sum refunds deducted from the invoice total, each {"amount": "...", "reason": "..."} (reason optional). Rendered as itemized rows in the summary followed by a "Total after Refund" row. See Refunds. | list of Refund Objects | Optional |
Item Object (invoice.items)
Represents a single line item on the invoice, found within the invoice.items array.
Example:
{
"item_type": "Service",
"description": "Graphic Design Services - Logo Package",
"quantity": "1",
"unit_price": "750.00",
"taxes": [1]
}If invoice.taxes defines one tax (e.g., Sales Tax at index 0), "taxes": [1] means this item is subject to that Sales Tax. If invoice.taxes was [ "name": "GST", "value": "5", "name": "PST", "value": "7"], then "taxes": [1, 0] would apply GST but not PST to this item.
Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| description | Detailed description of the product or service. | string | Required |
| quantity | The quantity of the item. If omitted, defaults to 1 for calculations if total is not provided. See Numeric & Currency for FlexibleDecimal details. | FlexibleDecimal | Optional |
| unit_price | The price per unit of the item. If omitted, defaults to 0 for calculations if total is not provided. See Numeric & Currency for FlexibleDecimal details. | FlexibleDecimal | Optional |
| item_type | A category or type for the item (e.g., "Service", "Product", "Hourly Rate"). Displayed if the column is active. | string | Optional |
| total | The total amount for this line item (quantity * unit_price). If provided, this value will be used directly, overriding any calculation. See Numeric & Currency. | FlexibleDecimal | Optional (calculated as quantity * unit_priceif omitted) |
| taxes | A list of flags (0 or 1) corresponding to the taxes defined in invoice.taxes. A 1 at a given index means the tax at that same index in invoice.taxes applies to this item. A 0 means it does not. For example, if invoice.taxes has two taxes defined, [1, 0] here means the first tax applies but the second does not. If omitted, no global taxes apply to this item. | list of integers (0 or 1) | Optional |
| refund | Marks this item as refunded. Pass {} for a simple full refund, or add an optional amount (partial refund) and reason. The row is visually highlighted and the refund is deducted in the summary. See Refunds. | ItemRefund Object | Optional |
Tax Object (within invoice.taxes)
Defines a specific tax type that can be applied to invoice items. These are listed in the invoice.taxes array. A maximum of 3 tax types can be defined per invoice.
Example:
{
"name": "State Sales Tax",
"value": "7.5"
}In this example, "value": "7.5" represents a 7.5% tax rate.
Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| name | The name of the tax (e.g., "VAT", "Sales Tax", "GST"). This will appear on the invoice. | string (max 50 chars) | Required |
| value | The tax rate as a percentage. For example, for a 7.5% tax, provide "7.5". See Numeric & Currency for FlexibleDecimal details. | FlexibleDecimal | Required |
Feature: Invoice Total Rounding
The API allows for precise rounding of the final invoice total to meet specific monetary conventions (e.g., Swiss rounding to the nearest 0.05 CHF, or rounding to whole currency units). This is controlled by two optional fields within the invoice object: rounding_target_cents and rounding_method.
rounding_target_cents
This field specifies the increment to which the final total should be rounded. It accepts one of the following literal integer values:
0: Rounds the total to the nearest whole currency unit (e.g., $10.07 becomes $10.00).5: Rounds the total to the nearest 0.05 of a currency unit (e.g., $10.07 becomes $10.05, $10.08 becomes $10.10).10: Rounds the total to the nearest 0.10 of a currency unit.20: Rounds the total to the nearest 0.20 of a currency unit.50: Rounds the total to the nearest 0.50 of a currency unit.
If rounding_target_cents is not provided, no rounding is applied to the final total beyond standard two-decimal precision for most currencies.
rounding_method
This field defines how the rounding is performed if rounding_target_cents is specified. It accepts one of the following string literals:
"closest": Rounds to the nearest target increment (standard rounding, half-up)."discount"(Default ifrounding_target_centsis set butrounding_methodis omitted): Always rounds the total down to the nearest target increment (floor)."penalty": Always rounds the total up to the nearest target increment (ceiling).
Rounding Adjustment on Invoice
If the rounding calculation results in a difference between the unrounded total and the rounded total, a line item typically labeled "Rounding Adjustment" (or its translation) will automatically appear in the invoice summary, showing the amount of the adjustment (e.g., "-$0.02" or "+$0.03"). The "Amount to be Paid" will reflect the final rounded total.
Example Scenario:
If an invoice's calculated total is $123.47 and you specify:
{
// ... other invoice fields
"rounding_target_cents": 5,
"rounding_method": "closest"
}The final amount to be paid will be $123.45, and a "Rounding Adjustment: -$0.02" line will be shown.
If the method was "penalty", the total would be $123.50 with an adjustment of "+$0.03".
If the method was "discount", the total would be $123.45 with an adjustment of "-$0.02".
Feature: Dates & Timestamps
The API offers flexibility in specifying and formatting dates for the invoice issue date and due date.
Date Precedence:
For both invoice date and due date, the API uses the following precedence:
- Pre-formatted String (
date/due_date): If you provide a string directly ininvoice.dateorinvoice.due_date, this string will be used as-is on the PDF. This gives you full control over the date's appearance. - Unix Timestamp (
date_timestamp/due_date_timestamp): If a pre-formatted string is not provided, but a Unix timestamp (seconds since epoch) is given ininvoice.date_timestamporinvoice.due_date_timestamp, the API will format this timestamp.- The formatting uses the pattern specified in
invoice.date_format(default:"d MMMM y"). - The formatting is localized using the resolved Babel locale (derived from
data.babel_locale,data.language, or defaults). - The timestamp is interpreted as UTC and then converted to the timezone specified in
data.timezone(default:"UTC") before formatting.
- The formatting uses the pattern specified in
- Automatic Date (for Invoice Date only): If neither
invoice.datenorinvoice.date_timestampis provided for the invoice issue date, the API will automatically use the current server date and time (UTC), formatted according toinvoice.date_format,data.babel_locale, anddata.timezone. The due date is never automatically generated; it will be omitted if not specified.
Date Formatting (invoice.date_format):
The invoice.date_format field accepts date format patterns supported by the Babel library (e.g., "MMMM d, yyyy", "dd.MM.yy", "y-MM-dd").
Timezones (data.timezone):
Use standard IANA timezone identifiers (e.g., "America/New_York", "Europe/Paris", "Asia/Tokyo"). If an invalid timezone is provided, UTC will be used with a warning.
Feature: Numeric & Currency
The API is designed to handle various numeric inputs for quantities and monetary values robustly.
Understanding FlexibleDecimal
Several monetary and quantity fields (like unit_price, discount_amount, quantity, tax.value) in the API are of type FlexibleDecimal. This custom type offers robust parsing for various numeric inputs:
- Standard Numbers: Direct JavaScript numbers (e.g.,
150,125.75) are accepted. - Numeric Strings: The API intelligently parses strings representing numbers, accommodating common formats:
"1234.50"(dot as decimal separator)"1,234.50"(comma as thousands separator, dot as decimal - common in the US)"1.234,50"(dot as thousands separator, comma as decimal - common in parts of Europe)"1'200.00"(apostrophe as thousands separator)"1 200.50"(space as thousands separator)
- An empty string (
"") ornullfor these fields is generally interpreted as0.00by the backend due to the custom validation logic.
Currency Formatting
- Babel Locale (
data.babel_locale): If provided (e.g.,"en_US","de_DE"), this locale is prioritized for formatting currency amounts according to standard conventions for that locale and the specifiedinvoice.currency. - Fallback Formatting (
data.decimal_separator,data.thousands_separator): Ifdata.babel_localeis not provided or is problematic, the API uses these custom separators along with theinvoice.currencycode for basic currency formatting. The number of decimal places is determined by standard currency precision (e.g., 2 for USD/EUR, 0 for JPY).
Integer-based Currency (data.use_int_for_currency)
If data.use_int_for_currency is set to true, all monetary FlexibleDecimal fields (e.g., unit_price, total, discount_amount in the invoice object and its sub-objects) must be provided as integers. These integers represent the value in the smallest currency unit (e.g., cents for USD, pence for GBP). For example, $123.45 would be sent as 12345. Quantities are not affected by this setting.
Feature: Logos & Branding
You can include a company logo on your invoices using one of the following methods:
- Image/SVG URL (
data.logo_link): Provide a publicly accessible URL to an image file (PNG, JPG recommended) or an SVG file.- The API will fetch this image. For SVGs, it attempts to extract the
viewBoxattribute to help with scaling. - Dimensions can be constrained using
data.logo_max_height,data.logo_max_width, and influenced bydata.logo_min_height. The aspect ratio is maintained.
- The API will fetch this image. For SVGs, it attempts to extract the
- Text Logo (
data.text_logo): Iflogo_linkis not provided or the image fails to load, andtext_logois set, this text will be displayed as a prominent heading where the logo would normally appear.
If neither is provided, the logo area will typically be left blank, or a default document title might appear depending on the template.
Feature: QR Codes
A QR code can be added to the invoice, typically used for payment links or links to online invoice versions.
- URL (
data.qr_link): A valid URL that the QR code will point to when scanned. If omitted, no QR code is generated. - Description (
data.qr_desc): Optional text displayed beneath the QR code (e.g., "Scan to Pay"). - Position (
data.qr_position): Can be"top"(on a separate page before the invoice content) or"bottom"(on a separate page after the invoice content, default).
Feature: Watermark
Add a customizable text watermark to invoices. This is useful for branding free-tier invoices, marking drafts, or adding promotional text.
Payload Location
Add at the root level of your data object:
{
"watermark": {
"text": "Powered by invoice.rocks",
"position": "bottom",
"font_size": 10,
"color": "#888888",
"opacity": 0.8
}
}Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| text | The watermark text to display on the invoice. | string (max 100 chars) | Required |
| position | Position of the watermark on the page. | "top" | "bottom" | "both" | "bottom" |
| font_size | Font size in pixels for the watermark text. | integer (6-20) | 10 |
| color | Hex color code for the watermark text (e.g., "#ff6600"). | string (hex) | "#888888" |
| opacity | Opacity value for the watermark text. | float (0.1-1.0) | 0.8 |
Rendering Behavior
- Top position: Rendered as a centered line above the invoice header
- Bottom position: Rendered as a centered line in the page footer
- Both: Renders at both top and bottom positions
Example Use Cases
Free tier branding:
{
"watermark": {
"text": "Powered by invoice.rocks",
"position": "bottom"
}
}Draft invoice:
{
"watermark": {
"text": "DRAFT - NOT FOR PAYMENT",
"position": "both",
"color": "#ff6600",
"opacity": 0.6
}
}Feature: Corner Ribbon
Add a diagonal ribbon in the top-right corner of the invoice to display status or important information such as PAID, OVERDUE, DRAFT, or custom messages.
Payload Location
Add at the root level of your data object:
{
"ribbon": {
"text": "PAID",
"style": "success"
}
}Or with custom colors:
{
"ribbon": {
"text": "VIP CLIENT",
"background_color": "#6f42c1",
"text_color": "#ffffff",
"border_color": "#5a32a3"
}
}Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| text | Text to display on the corner ribbon (e.g., "PAID", "DRAFT", "OVERDUE"). | string (max 15 chars) | Required |
| style | Predefined style for the ribbon. If provided, overrides custom colors. | "info" | "success" | "warning" | "danger" | Optional |
| background_color | Custom background color for the ribbon (hex code). Used when style is not set. | string (hex) | Optional |
| text_color | Custom text color for the ribbon (hex code). Used when style is not set. | string (hex) | Optional |
| border_color | Custom border color for the ribbon (hex code). Used when style is not set. | string (hex) | Optional |
Predefined Styles
| Style | Background | Use Case |
|---|---|---|
success | Green (#28a745) | Paid, Completed, Approved |
info | Blue (#17a2b8) | Processing, Pending, Info |
warning | Yellow (#ffc107) | Due Soon, Attention, Review |
danger | Red (#dc3545) | Overdue, Cancelled, Rejected |
Example Use Cases
Paid invoice:
{
"ribbon": {
"text": "PAID",
"style": "success"
}
}Overdue invoice:
{
"ribbon": {
"text": "OVERDUE",
"style": "danger"
}
}Feature: Paid Amount
Display partial payment information on invoices, showing the amount already paid and the remaining balance due. This is essential for sending updated invoices after partial payments.
Payload Location
Add within the invoice object:
{
"invoice": {
"id": "INV-2024-001",
"currency": "USD",
"items": [...],
"paid_amount": "500.00"
}
}Rendering Behavior
When paid_amount is provided and greater than 0, the invoice summary section displays:
Subtotal: $1,000.00
Discount (10%): -$100.00
Tax (VAT 19%): $171.00
─────────────────────────────────────
Total: $1,071.00
Amount Paid: -$500.00
─────────────────────────────────────
Balance Due: $571.00Validation
The paid_amount value must be a non-negative number. If it exceeds the invoice total, the balance due will show as a negative value (credit to the customer).
Translation Keys
The following translation keys are used for this feature:
amount_paid- Label for the paid amount row (default: "Amount Paid")balance_due- Label for the remaining balance (default: "Balance Due")
Feature: Refunds
Mark individual line items as refunded (fully or partially) and/or apply lump-sum refunds against the invoice total (e.g., goodwill for a late delivery) — each with an optional reason. Refunded items are visually highlighted, and all refunds are itemized in the totals summary.
Item-level refunds
Add refund to any item in invoice.items. An empty object is a simple full refund:
{
"invoice": {
"items": [
{ "description": "Server hosting", "quantity": 1, "unit_price": "89.90",
"refund": {} },
{ "description": "Express delivery", "quantity": 1, "unit_price": "40.00",
"refund": { "amount": "20.00", "reason": "Arrived late" } }
]
}
}ItemRefund Object Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| reason | Refund reason shown under the item description (e.g., "Refunded — Arrived late"). | string (max 200 chars) | Optional |
| amount | Gross amount refunded for this item (incl. tax), for partial refunds. If omitted, the item's full total is refunded. | FlexibleDecimal | Full item total |
Lump-sum refunds
Add refunds (up to 10) within the invoice object. Each entry renders as its own summary row:
{
"invoice": {
"refunds": [
{ "amount": "50.00", "reason": "Goodwill - late delivery" }
]
}
}Refund Object Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| amount | Gross refund amount deducted from the invoice total. | FlexibleDecimal | Required |
| reason | Refund reason shown in parentheses next to the "Refund" summary row. | string (max 200 chars) | Optional |
Display configuration
Optionally add refund_display at the root level of your data object to customize the highlighting:
{
"refund_display": {
"striped": true,
"strikethrough": true,
"color": "#dc3545",
"background_color": "#fdecea"
}
}RefundDisplay Object Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| striped | Renders a diagonal-striped background on refunded item rows. | boolean | true |
| strikethrough | Strikes through the amounts of fully refunded items. Partial refunds are never struck through; they show the refunded amount in the note instead. | boolean | true |
| color | Color of refund notes and refund amounts. Use a green like "#1e7e34" for a friendlier "money back" tone. | string (hex) | "#dc3545" |
| background_color | Tint of the stripes on refunded item rows. | string (hex) | "#fdecea" |
Rendering Behavior & Calculation
- Refunded item rows get a striped background and a colored note under the description ("Refunded — reason"). Fully refunded items have their amounts struck through; partial refunds show the refunded amount in the note.
- Refund amounts are gross (incl. tax) and are deducted from the final total. The subtotal, discounts, and tax rows remain exactly as originally billed.
- Summary order: Total → refund rows (item refunds are aggregated into one "Refunded Items" row, each lump-sum refund gets its own row) → "Total after Refund" → Amount Paid / Balance Due (if
paid_amountis set). The balance due is computed from the total after refunds. - With
use_int_for_currency: true, refund amounts are also treated as integers of the smallest currency unit.
Subtotal: 1,629.90 CHF
VAT (8.10%): 132.02 CHF
──────────────────────────────────────────────
Amount to be Paid: 1,761.92 CHF
Refunded Items: -109.90 CHF
Refund (Goodwill - late): -50.00 CHF
──────────────────────────────────────────────
Total after Refund: 1,602.02 CHF
Amount Paid: -500.00 CHF
Balance Due: 1,102.02 CHFTranslation Keys
refunded- Note under a refunded item (default: "Refunded")refund- Lump-sum refund summary row (default: "Refund")refunded_items- Aggregated item-refund row (default: "Refunded Items")total_after_refund- Net total row (default: "Total after Refund")
Feature: Swiss QR Bill
Render a spec-conformant Swiss QR bill payment part (QR-Rechnung, 210×105 mm receipt + payment part with the Swiss QR code) directly in the generated PDF. Presence of the qr_bill object enables the feature. This is independent from the generic QR Codes feature — both can be used together.
Payload Location
Add at the root level of your data object. Minimal example (regular IBAN — the amount is auto-filled with the invoice balance due):
{
"qr_bill": {
"iban": "CH58 0079 1123 0008 8901 2",
"creditor": {
"name": "Example AG",
"street": "Musterstrasse",
"house_number": "1",
"postal_code": "3000",
"city": "Bern"
}
}
}Full example with a QR-IBAN, QR reference, and debtor:
{
"qr_bill": {
"iban": "CH44 3199 9123 0008 8901 2",
"creditor": { "name": "Example AG", "street": "Musterstrasse", "house_number": "1", "postal_code": "3000", "city": "Bern" },
"debtor": { "name": "Hans Beispiel", "street": "Beispielweg", "house_number": "2", "postal_code": "8000", "city": "Zürich" },
"reference": "21 00000 00003 13947 14300 09017",
"additional_information": "Invoice INV-2024-001",
"position": "new_page_bottom"
}
}QrBill Object Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| iban | CH/LI IBAN or QR-IBAN of the creditor account (21 characters, spaces allowed, checksum validated). A QR-IBAN (institution ID 30000–31999) requires a 27-digit QR reference; a regular IBAN takes an RFxx Creditor Reference or no reference. | string | Required |
| creditor | Payee (account holder) address. See address table below. | QrBillAddress Object | Required |
| debtor | Payer address. If omitted, the payment part shows blank corner-mark fields for handwriting. | QrBillAddress Object | Optional |
| amount | Payment amount. Defaults to the invoice balance due (total after refunds and paid amount). Pass 0 to force an open amount (blank field). | FlexibleDecimal | Balance due |
| currency | Payment currency. Defaults to the invoice currency when it is CHF or EUR; required otherwise. | "CHF" | "EUR" | Invoice currency |
| reference | 27-digit QR reference (requires a QR-IBAN) or RFxx Creditor Reference (ISO 11649). Spaces allowed; checksums validated. | string | Optional |
| additional_information | Unstructured message shown on the payment part (e.g., the invoice number). | string (max 140 chars) | Optional |
| billing_information | Structured SWICO billing information for automated processing. | string (max 140 chars) | Optional |
| language | Language of the payment part labels. Defaults to the invoice language when supported, else English. | "en" | "de" | "fr" | "it" | Invoice language |
| position | Placement of the QR bill in the PDF. See "Positions" below. | "new_page_top" | "inline" | "new_page_bottom" | "new_page_bottom" |
| top_line | Renders the dashed separation line with scissors above the payment part. | boolean | true |
QrBillAddress Object Properties:
| Property | Description | Type | Default |
|---|---|---|---|
| name | Name of the party (person or company). | string (max 70 chars) | Required |
| street | Street name, without the building number. | string (max 70 chars) | Optional |
| house_number | Building / house number. | string (max 16 chars) | Optional |
| postal_code | Postal code. | string (max 16 chars) | Required |
| city | City. | string (max 35 chars) | Required |
| country | ISO 3166-1 alpha-2 country code. | string (2 chars) | "CH" |
Positions
"new_page_bottom"(default): Dedicated last page with the payment part flush at the bottom — the standard, detachable Swiss layout."new_page_top": Dedicated first page (payment part at the bottom of that page), followed by the invoice."inline": Best effort: rendered full-width in the document flow right after the notes. If it doesn't fit on the current page, it moves to the next page as a whole.
IBAN vs. QR-IBAN
Both work — they are validated and handled automatically, but they follow different reference rules per the Swiss Payment Standards:
- Regular IBAN (e.g.,
CH58 0079 ...): use an RFxx Creditor Reference (ISO 11649) inreference, or omit the reference entirely. - QR-IBAN (institution ID 30000–31999, e.g.,
CH44 3199 ...): a 27-digit QR reference is mandatory. Your bank issues QR reference numbers for automated reconciliation.
Mismatches (QR reference with a regular IBAN, QR-IBAN without a QR reference, invalid checksums) are rejected at validation time with a descriptive error.
Amount & Currency Defaults
- Amount: defaults to the invoice balance due — the final total after rounding, refunds, and
paid_amount. Pass an explicitamountto override, or0for an open amount field. - Currency: QR bills only support CHF and EUR. If the invoice currency is CHF or EUR it is used automatically; otherwise you must pass
qr_bill.currencyexplicitly (and normally an explicitamounttoo, since the invoice total is in another currency).
Feature: Delivery Notes & Custom Documents
Beyond standard invoices, the API can produce delivery notes (Lieferschein / bolla di consegna), dispatch confirmations, and similar order documents. These fields make this possible:
data.document_title: Prints a large document heading (e.g., "Lieferschein") above the details table.data.invoice.custom_fields: Adds your own label/value rows to the details header — order date, dispatch date, references, contact person, phone, etc.data.hide_amounts: Removes the unit price and amount columns and the entire totals summary, for price-free documents. Item prices may simply be omitted from the payload too; this flag guarantees no money is printed even if they are present.data.items_title: Prints a customizable heading directly above the items table (e.g., "In this order") to introduce the article list.data.item_checkboxes: Adds an empty checkbox at the start of each item row so staff can physically tick off each article when packing or verifying a box.
Combine these with ship_to, per-address label overrides, and the top-level lang object (see Translations & Text) to fully control the document.
Example: German delivery note (Lieferschein)
{
"data": {
"language": "de",
"document_title": "Lieferschein",
"hide_amounts": true,
"items_title": "In dieser Lieferung",
"item_checkboxes": true,
"from": { "name": "Händler GmbH", "street": "Industriestrasse 1", "place": "8005 Zürich", "country": "Schweiz" },
"ship_to": {
"label": "Empfangsperson",
"name": "Firma ABC",
"extra": "B. Meier, PF 3820",
"street": "Sonnenstrasse 13",
"place": "CH-5000 Aarau",
"country": "Schweiz",
"address_lines_order": ["name", "extra", "street", "place", "country"]
},
"invoice": {
"id": "LS-2026-001",
"purchase_order": "18531791",
"currency": "CHF",
"date": "16.2.2021",
"custom_fields": [
{ "label": "Bestelldatum", "value": "14.2.2021" },
{ "label": "Versanddatum", "value": "16.2.2021" },
{ "label": "Ihre Referenzperson", "value": "M. Haller" },
{ "label": "Ihre Referenz", "value": "INT-3157" }
],
"items": [
{ "item_type": "10121799", "description": "Nevox Carbon Cover", "quantity": "1" }
],
"notes": "Die Zahlung erfolgte bereits direkt an den Marktplatz."
}
},
"lang": {
"id_invoice": "Lieferschein-Nr.",
"invoice_of": "Datum",
"item_type": "Artikelnummer"
}
}Omit hide_amounts (or set it to false) to keep prices and totals on the document — useful when the delivery document doubles as an invoice. The items_title + item_checkboxes combination turns the document into a pick/pack checklist your warehouse staff can tick off by hand.
Feature: Translations & Text
The API provides flexibility in how static text elements (like "Due Date", "Subtotal", etc.) are displayed on the PDF.
1. Base Language (data.language)
The data.language field (e.g., "en", "es", "de") determines the base set of translations used for the invoice. If a specific language isn't supported, it defaults to English.
An example structure of the default English translations is:
{
"from": "From",
"to": "To",
"ship_to": "Ship to",
"id_invoice": "Invoice #",
"invoice_of": "Invoice Date",
"due_date": "Due Date",
"subject": "Subject",
"item_type": "Type",
"description": "Description",
"quantity": "Qty",
"unit_price": "Unit Price",
"totals": "Amount",
"note": "Note",
"free": "Free",
"subtotal": "Subtotal",
"discount": "Discount",
"amount_to_be_paid": "Amount to be Paid",
"rounding_difference": "Rounding Adjustment"
}2. Per-Request Overrides (top-level lang object)
You can override any of these base translations for a specific invoice by providing a lang object at the top level of your request body (as a sibling to the data object). Keys in this lang object will replace the corresponding keys from the loaded base language file for that specific request.
Example of Overriding Text:
If your data.language is "en", but for a particular invoice you want "Due Date" to appear as "Payment Expected By" and "Item Type" as "Category", your request body would look like this:
{
"data": {
"language": "en",
// ... other data fields ...
"invoice": {
// ... invoice fields ...
}
},
"lang": {
"due_date": "Payment Expected By",
"item_type": "Category",
"custom_key_if_template_supports_it": "Custom Value"
}
}This provides fine-grained control over the text displayed on your invoices without needing to modify base language files globally.
Example Payload & API Calls
This section provides a complete example JSON payload (specifically, the content for the data key) and demonstrates how to send it to the API using various programming languages. Remember to replace YOUR_CLIENT_API_KEY_HERE with your valid client API key.
Full Example data Object Payload:
This payload showcases many available fields, including the new rounding feature and an omitted optional field (to.street). Your actual payload might be simpler. This example adheres to US conventions for currency, locale, and formatting.
{
"language": "en",
"babel_locale": "en_US",
"timezone": "America/New_York",
"decimal_separator": ".",
"thousands_separator": ",",
"use_int_for_currency": false,
"qr_position": "bottom",
"from": {
"name": "USA Example Corp.",
"street": "100 Main Street",
"place": "New York, NY 10001",
"country": "United States",
"extra": "Suite 500",
"address_lines_order": ["name", "extra", "street", "place", "country"]
},
"to": {
"name": "Client Solutions Inc.",
"place": "Los Angeles, CA 90012",
"country": "United States",
"extra": "Attn: Accounts Payable"
},
"ship_to": {
"label": "Deliver To",
"name": "Client Solutions Inc. - Warehouse",
"street": "2200 Logistics Way",
"place": "Long Beach, CA 90802",
"country": "United States"
},
"text_logo": "USA Example",
"qr_link": "https://example.com/invoice/INV-2024-001/pay",
"qr_desc": "Scan to view or pay your invoice",
"logo_link": "https://raw.githubusercontent.com/贄Dev/PublicElements/main/logo_placeholder_250x60_dark.png",
"logo_max_height": 60,
"logo_max_width": 250,
"logo_min_height": 40,
"invoice": {
"id": "INV-2024-001",
"purchase_order": "PO-CLIENT-12345",
"currency": "USD",
"discount_amount": "25.00",
"discount_percentage": "2.5",
"taxes_method": "excluded",
"date_timestamp": 1721001600,
"due_date": "August 15, 2024",
"date_format": "MMMM d, yy",
"subject": "Q3 Software Development Services",
"notes": "Thank you for your business! Payment is due within 30 days of the invoice date. Please reference invoice ID with your payment.",
"taxes": [
{"name": "State Sales Tax", "value": "6.25"},
{"name": "Local Surcharge", "value": "0.5"}
],
"items": [
{
"item_type": "Service",
"description": "Software Development Hours (Task A)",
"quantity": "80",
"unit_price": "125.00",
"taxes": [1, 0]
},
{
"item_type": "Product",
"description": "Annual Software License - Tier 2",
"quantity": "1",
"unit_price": "1500.00",
"total": "1450.00",
"taxes": [1, 1]
}
],
"rounding_target_cents": 5,
"rounding_method": "closest"
}
}When sending this to the API, it should be wrapped within a top-level JSON object under the "data" key, like so: { "data": {
"language": "en",
"babel_locale": "en_US",
... } . The code examples below handle this wrapping.
Sending the Payload:
const API_URL = 'https://api.json2invoice.com';
const API_KEY = 'YOUR_CLIENT_API_KEY_HERE';
const invoiceData = {
"language": "en",
"babel_locale": "en_US",
"timezone": "America/New_York",
"decimal_separator": ".",
"thousands_separator": ",",
"use_int_for_currency": false,
"qr_position": "bottom",
"from": {
"name": "USA Example Corp.",
"street": "100 Main Street",
"place": "New York, NY 10001",
"country": "United States",
"extra": "Suite 500",
"address_lines_order": ["name", "extra", "street", "place", "country"]
},
"to": {
"name": "Client Solutions Inc.",
"place": "Los Angeles, CA 90012",
"country": "United States",
"extra": "Attn: Accounts Payable"
},
"ship_to": {
"label": "Deliver To",
"name": "Client Solutions Inc. - Warehouse",
"street": "2200 Logistics Way",
"place": "Long Beach, CA 90802",
"country": "United States"
},
"text_logo": "USA Example",
"qr_link": "https://example.com/invoice/INV-2024-001/pay",
"qr_desc": "Scan to view or pay your invoice",
"logo_link": "https://raw.githubusercontent.com/贄Dev/PublicElements/main/logo_placeholder_250x60_dark.png",
"logo_max_height": 60,
"logo_max_width": 250,
"logo_min_height": 40,
"invoice": {
"id": "INV-2024-001",
"purchase_order": "PO-CLIENT-12345",
"currency": "USD",
"discount_amount": "25.00",
"discount_percentage": "2.5",
"taxes_method": "excluded",
"date_timestamp": 1721001600,
"due_date": "August 15, 2024",
"date_format": "MMMM d, yy",
"subject": "Q3 Software Development Services",
"notes": "Thank you for your business! Payment is due within 30 days of the invoice date. Please reference invoice ID with your payment.",
"taxes": [
{"name": "State Sales Tax", "value": "6.25"},
{"name": "Local Surcharge", "value": "0.5"}
],
"items": [
{
"item_type": "Service",
"description": "Software Development Hours (Task A)",
"quantity": "80",
"unit_price": "125.00",
"taxes": [1, 0]
},
{
"item_type": "Product",
"description": "Annual Software License - Tier 2",
"quantity": "1",
"unit_price": "1500.00",
"total": "1450.00",
"taxes": [1, 1]
}
],
"rounding_target_cents": 5,
"rounding_method": "closest"
}
};
async function generatePdf() {
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
body: JSON.stringify({ data: invoiceData })
});
const result = await response.json();
if (response.ok) {
console.log("Success:", result);
} else {
console.error("Error:", response.status, result);
}
} catch (error) {
console.error('Request failed:', error);
}
}
generatePdf();
Detailed API Responses
Success Response (200 OK)
Upon successful PDF generation and upload to our secure storage, the API will return a JSON object with the following structure:
{
"message": "PDF generated and uploaded successfully.",
"url": "https://json-invoices.s3.amazonaws.com/INV-2025001_20250727213106332860Z_2e3...",
"object_key": "INV-2025001_20250727213106332860Z_2e3f28a5b2284a3da3a84fc83bcefeff.pdf",
"file_size": 23958
}Note: The URL structure is illustrative; the actual filename will include a unique timestamp and identifier. The URLs are presigned S3 URLs for secure access to the generated PDFs.
If invoice total rounding was applied (via rounding_target_cents) and resulted in a change to the total, a line item such as "Rounding Adjustment" will be visible in the PDF's summary section, showing the difference.
Fields:
| Property | Description | Type | Default |
|---|---|---|---|
| message | A confirmation message indicating success. | string | - |
| url | The publicly accessible URL to the generated PDF file. | string | - |
| object_key | The file name of the generated PDF on the server. | string | - |
| file_size | The size of the generated PDF file in bytes. | integer | - |
Error Responses
400 Bad Request: Invalid Input
This status code indicates an issue with the data sent by your application. Common causes include:
- Missing or Malformed JSON:
{ "error": "No data provided." }{ "error": "Invalid JSON format: <parsing error details>" } - Missing
dataKey: The top-level request object must contain adatakey.{ "error": "Missing 'data' key in payload." } - Payload Validation Errors: The content of the
dataobject failed validation against the defined schema.{ "error": "Invalid payload structure", "details": [ /* Array of Pydantic validation error objects */ ] }The
detailsarray is crucial for debugging. It provides specific information about each validation failure:For 400 Bad Request (Invalid Payload Structure)
Property Description Type Default error General error message indicating a problem with the submitted data. string"Invalid payload structure"details An array providing specific details about each validation error encountered. array of objects- details[].loc Indicates the location (path) of the field within the input payload that caused the error (e.g., ["invoice", "items", 0, "quantity"]).array- details[].msg A human-readable message describing the validation error. string- details[].type The type of validation error (e.g., "value_error.decimal.finite","missing").string-
403 Forbidden: Authentication or Authorization Issue
This error signifies that your request was understood, but the server is refusing to fulfill it due to authentication or authorization problems.
- Missing, Invalid, or Unauthorized API Key:
{ "error": "Forbidden: Missing or invalid API Key." }{ "error": "Forbidden: Missing or incomplete authentication context." }
500 Internal Server Error: Server-Side Problem
This indicates an unexpected issue occurred on our server while attempting to process your request (e.g., during PDF rendering, file storage, or other internal operations).
{
"error": "An unexpected error occurred while processing your request.",
"trace": "Internal server error. Check logs."
}If our server is in a special debug mode (not typical for production), the trace might contain more detailed error information. Otherwise, please refer to server logs or contact support with any provided request identifiers or timestamps.
Note on Partial Success
In a rare scenario where the PDF is successfully generated and stored, but a subsequent, non-critical internal operation (like decrementing an API usage credit) fails, the API will still return a 200 OK response with the PDF URL. This ensures you receive your generated document. A critical warning will be logged on our server for this situation, which we will investigate.
This documentation provides a comprehensive guide to integrating with the PDF Invoice Generation API. Should you have further questions or require assistance, please consult our support channels.
