The Invoice PDF API generates invoices, receipts, quotes, and related billing documents as PDFs from structured JSON. Supply document details, parties, line items, and totals in the request.
PDFCrowd renders the supplied data using its predefined document layouts. Request options select the layout, page settings, and localized formatting.
| Need | Action |
|---|---|
| Test the API quickly | Go to the First API Request and run the sample JSON request. |
| Use your own invoice data | Follow the Invoice Generator alternative, export starter JSON, then adapt it for repeated API requests. |
| Integrate invoice PDFs | Send invoice JSON to the API endpoint. |
Starter JSON files are available in Document Types.
Save this sample invoice JSON to invoice.json:
To begin with your own representative invoice instead, see the Invoice Generator alternative after the request details.
{ "document_type": "invoice", "document_number": "INV-2026-001", "issue_date": "2026-01-15", "due_date": "2026-02-14", "currency": "USD", "seller": { "name": "Acme Corp", "email": "billing@acmecorp.com", "address": { "address1": "100 Market St", "city": "San Francisco", "province": "CA", "zip": "94105", "country": "United States", "country_code": "US" } }, "customer": { "name": "Jane Cooper", "company": "Cooper Design LLC", "email": "jane@cooperdesign.com", "address": { "address1": "456 Oak Ave", "city": "Austin", "province": "TX", "zip": "73301", "country": "United States", "country_code": "US" } }, "items": [ {"description": "Consulting Services", "quantity": 5, "unit_price": 150.00}, {"description": "Implementation Workshop", "quantity": 1, "unit_price": 300.00} ], "subtotal": 1050.00, "tax": { "amount": 84.00, "label": "Sales Tax" }, "total": 1134.00 }
Run the request below to save the PDF as invoice.pdf:
Run the request in a Bash-compatible shell.
-u supplies your username and API key.-H sets the request content type to application/json.--data-binary @filename sends the JSON file's contents as the POST body.-o saves the returned PDF to a file.-f makes HTTP errors fail the command.-s -S hides the progress meter while keeping error messages.
Install Requests with python3 -m pip install requests.
Save a request as invoice.py and run it with
python3 invoice.py.
auth supplies your username and API key using HTTP Basic authentication.data sends the JSON file opened in binary mode as the request body. The Content-Type header identifies it as JSON.allow_redirects=False returns the endpoint's response without following redirects.raise_for_status() raises an exception for HTTP errors before opening the output file.response.content contains the PDF bytes, saved to a file opened with 'wb'.
The with blocks close opened files. Connection errors also raise
exceptions. Allow for PDF generation time if you add a request timeout.
Use Node.js with its built-in Fetch API; no HTTP package is needed.
Save a request as invoice.mjs and run it with
node invoice.mjs. The .mjs extension enables imports
and top-level await.
Authorization sends your username and API key using HTTP Basic authentication.readFile reads the JSON file as bytes for the request body. The Content-Type header identifies it as JSON.redirect: 'manual' returns the endpoint's response without following redirects.response.ok checks for a successful HTTP status before saving the PDF. Fetch does not throw for HTTP errors itself.arrayBuffer() reads the response bytes; writeFile saves them to the output file.File and connection errors propagate as exceptions.
Install Axios with npm install axios. Save a request as
invoice.mjs and run it with node invoice.mjs.
The .mjs extension enables imports and top-level await.
auth supplies your username and API key using HTTP Basic authentication.readFile reads the JSON file as bytes for the POST body. The Content-Type header identifies it as JSON.responseType: 'arraybuffer' preserves the PDF as bytes.maxRedirects: 0 returns the endpoint's response without following redirects.writeFile saves the PDF after Axios accepts the HTTP response.Failed requests and file writes produce an error message and a nonzero exit status. The error handler prints only the message, keeping request credentials out of the error output. Allow for PDF generation time if you add a timeout.
Install Guzzle with composer require guzzlehttp/guzzle.
Save a request as invoice.php beside your vendor
directory and run it with php invoice.php.
auth supplies your username and API key using HTTP Basic authentication.Utils::tryFopen opens the JSON file in binary mode for the request body. The Content-Type header identifies it as JSON.'allow_redirects' => false returns the endpoint's response without following redirects.'http_errors' => false lets the example check the HTTP status before saving the PDF.file_put_contents saves the response bytes and checks that the entire PDF was written.Failed requests and file writes produce an error message and a nonzero exit status. Allow for PDF generation time if you add a timeout.
curl -f -s -S \ -u 'demo':'demo' \ -H 'Content-Type: application/json' \ --data-binary @invoice.json \ -o invoice.pdf \ https://api.pdfcrowd.com/templates/business/
import requests with open('invoice.json', 'rb') as invoice_file: response = requests.post( 'https://api.pdfcrowd.com/templates/business/', auth=('demo', 'demo'), headers={'Content-Type': 'application/json'}, data=invoice_file, allow_redirects=False, ) response.raise_for_status() with open('invoice.pdf', 'wb') as output_file: output_file.write(response.content)
import { readFile, writeFile } from 'node:fs/promises'; const username = 'demo'; const apiKey = 'demo'; const response = await fetch("https://api.pdfcrowd.com/templates/business/", { method: 'POST', headers: { Authorization: 'Basic ' + Buffer.from(username + ':' + apiKey).toString('base64'), 'Content-Type': 'application/json', }, body: await readFile("invoice.json"), redirect: 'manual', }); if (!response.ok) { throw new Error('HTTP ' + response.status + ' ' + response.statusText); } await writeFile("invoice.pdf", Buffer.from(await response.arrayBuffer()));
import axios from 'axios'; import { readFile, writeFile } from 'node:fs/promises'; const username = 'demo'; const apiKey = 'demo'; try { const response = await axios.post( "https://api.pdfcrowd.com/templates/business/", await readFile("invoice.json"), { auth: { username, password: apiKey }, headers: { 'Content-Type': 'application/json' }, responseType: 'arraybuffer', maxRedirects: 0, }, ); await writeFile("invoice.pdf", response.data); } catch (error) { console.error(error.message); process.exitCode = 1; }
<?php require 'vendor/autoload.php'; use GuzzleHttp\Client; use GuzzleHttp\Psr7\Utils; try { $client = new Client(); $response = $client->post('https://api.pdfcrowd.com/templates/business/', [ 'auth' => ['demo', 'demo'], 'headers' => ['Content-Type' => 'application/json'], 'body' => Utils::tryFopen('invoice.json', 'rb'), 'allow_redirects' => false, 'http_errors' => false, ]); $status = $response->getStatusCode(); if ($status < 200 || $status >= 300) { throw new \RuntimeException("HTTP $status " . $response->getReasonPhrase()); } $body = (string) $response->getBody(); if (file_put_contents('invoice.pdf', $body) !== strlen($body)) { throw new \RuntimeException('Failed to write output file'); } } catch (\Throwable $error) { fwrite(STDERR, $error->getMessage() . "\n"); exit(1); }
You can run the request above with the displayed API credentials. Update the invoice data and filenames as needed.
Send a POST request with a request body to the endpoint below.
POST https://api.pdfcrowd.com/templates/business/
On success, returns application/pdf.
On error, returns a plain-text message with an HTTP error status. See
Error Handling.
The API uses HTTP Basic authentication. Use your PDFCrowd username as the username and your API key as the password.
For testing, use username demo
and API key demo
(demo:demo
in cURL).
The prepared invoice.json above is the fastest way to evaluate
the API. If you prefer to begin with your own data, use the
Invoice PDF Generator as an
alternative request builder:
Submit each derived JSON document to the endpoint above using your API credentials. The exported file is starter JSON for your integration, not a finished static invoice to reuse unchanged.
The request body is structured invoice JSON that describes the document, parties, line items, totals, and rendering options. Here is the overall shape of an invoice document:
Numeric fields accept JSON numbers or numeric strings, for example
"quantity": 3 or "quantity": "3". Do not include
currency symbols or thousand separators.
{
"document_type": "invoice",
"document_number": "INV-2026-001",
"issue_date": "2026-03-15",
"currency": "USD",
"seller": { ... seller details ... },
"customer": { ... customer details ... },
"items": [ ... line items ... ],
"subtotal": 1440.00,
"discount": { ... discount ... },
"shipping": { ... shipping ... },
"tax": { ... tax ... },
"total": 1608.80,
"notes": "...",
"legal_notice": "...",
... document-type-specific fields ...
}
The sections below describe each part in detail.
| Field | Type | Description |
|---|---|---|
document_type | string | invoice, receipt, quote, refund, credit_note, or cancellation |
document_number | string | Document identifier. Example: "INV-2026-001". |
issue_date | string | Document date (YYYY-MM-DD) |
items | array | Line items. See Line Items. |
total | number | Grand total amount |
Unless noted otherwise, optional fields can be used with all document types. Document-type-specific fields are listed in Document Types.
| Field | Type | Description |
|---|---|---|
currency | string | ISO 4217 currency code. Examples: USD, EUR. Determines currency symbol, number formatting, and document language. Defaults to en_US formatting when omitted. See Localization. |
locale | string | Overrides the language and region derived from currency. Use a language code such as en or fr, or a language plus ISO 3166-1 alpha-2 region such as en_GB, fr_FR, nl_NL, or de_DE. The region affects date/number formatting and locale-specific identifier labels. See Localization. |
subtotal | number | Subtotal before adjustments. Hidden when equal to total with no breakdown rows |
seller | object | Seller/company details. See Seller. |
customer | object | Customer details. See Customer. |
discount | object | Discount details. See Discount. |
shipping | object | Shipping details. See Shipping. |
tax | object | Tax details. See Tax. |
notes | string | Free-text notes displayed at the bottom |
legal_notice | string | Legal disclaimer text |
style | string | Visual theme: modern (default), classic, or minimal. See Render Options. |
api_options | object | Page and layout options such as page_size and orientation. See Render Options. |
Visual style and page setup are part of the request body: a top-level
style field selects the theme, and page_size /
orientation go inside api_options.
{ "document_type": "invoice", "document_number": "INV-2026-001", "...": "...", "style": "classic", "api_options": {"page_size": "Letter", "orientation": "landscape"} }
| Option | Where | Default | Values |
|---|---|---|---|
style | top-level field | modern | modern, classic, minimal |
page_size | inside api_options | A4 | A4, Letter |
orientation | inside api_options | portrait | portrait, landscape |
api_options also accepts other PDFCrowd HTML-to-PDF options
(e.g. header_html, footer_html,
margin_top).
The three style themes:
| Style | Description |
|---|---|
modern | Clean sans-serif design with blue accents, colored table headers, alternating row colors |
classic | Traditional serif font, double borders, formal document layout |
minimal | Compact, text-focused — no logo, tighter spacing, smaller fonts |
"seller": { "name": "Acme Corp", "email": "billing@acmecorp.com", "phone": "+1 212 555 0199", "website": "https://acmecorp.com", "tax_id": "EIN 12-3456789", "logo_url": "https://acmecorp.com/logo.png", "address": { "address1": "100 Market St", "address2": "Suite 300", "city": "San Francisco", "province": "CA", "zip": "94105", "country": "United States", "country_code": "US" } }
| Field | Type | Description |
|---|---|---|
name | string | Company or shop name |
email | string | Contact email |
phone | string | Contact phone |
website | string | Website URL |
tax_id | string | Tax ID / VAT number. Printed with a locale-correct label such as DIČ, USt-IdNr., P.IVA, or GSTIN |
company_id | string | Company registration number. Printed above the tax ID with a locale-correct label. Examples include Czech IČO, French SIREN, and UK company number. |
registration_info | string | Commercial-register line printed below the seller’s tax ID. Example: “Registered in England and Wales, company number 12345678” |
bank_account | string | Bank account number. Shown on invoices and quotes |
iban | string | IBAN. Shown on invoices and quotes |
bic | string | BIC / SWIFT code. Shown on invoices and quotes |
logo_url | string | Company logo. Use an http(s) image URL or an embedded image as a data: URI. Example: data:image/png;base64,… |
address | object | Structured address. See Address. |
"customer": { "name": "John Smith", "email": "john@example.com", "phone": "+1 555 0100", "company": "Smith & Co.", "tax_id": "US-TAX-001", "address": { "address1": "123 Main St", "address2": "Suite 4B", "city": "New York", "province": "NY", "zip": "10001", "country": "United States", "country_code": "US" } }
| Field | Type | Description |
|---|---|---|
name | string | Customer full name |
email | string | Customer email |
phone | string | Customer phone |
company | string | Company name |
tax_id | string | Customer tax ID / VAT number |
company_id | string | Customer company registration number |
address | object | Structured address. See Address. |
Used within seller, customer, and shipping objects.
The country_code determines postal code placement —
e.g. DE formats as 10117 Berlin while
US formats as New York, NY 10001.
| Field | Type | Description |
|---|---|---|
address1 | string | Street address line 1 |
address2 | string | Address line 2, such as suite or unit |
city | string | City |
province | string | State / Province |
zip | string | ZIP / Postal code |
country | string | Country name. Displayed in the address |
country_code | string | ISO 3166-1 alpha-2 code. Used for formatting, not displayed |
"items": [ {"description": "Web Design", "quantity": 10, "unit_price": 120.00, "sku": "SVC-01"}, {"description": "Hosting", "quantity": 1, "unit_price": 240.00, "tax": 45.60} ]
| Field | Type | Description |
|---|---|---|
description | string | Required. Item name |
quantity | number | Required. Quantity |
unit_price | number | Required. Price per unit |
sku | string | SKU or product code. Adds a SKU column when present |
tax_rate | number | Per-line tax rate in percent. Informational; the document shows the totals and tax breakdown you supply |
tax | number | Tax for this item. Adds a Tax column when present |
total | number | Item total. Auto-computed as quantity × unit_price + tax when omitted |
"discount": { "amount": 100.00, "label": "Loyalty discount 10%" }
| Field | Type | Description |
|---|---|---|
amount | number | Discount amount. Displayed with a minus sign |
label | string | Custom label. Defaults to “Discount” in document language |
Available on all document types.
"shipping": { "amount": 15.00, "name": "Express Shipping", "recipient": "Jane Cooper", "address": { "address1": "456 Oak Ave", "city": "Austin", "province": "TX", "zip": "73301", "country": "United States", "country_code": "US" } }
| Field | Type | Description |
|---|---|---|
amount | number | Shipping cost. Omitted from document when zero |
name | string | Shipping method name. Shown as the shipping row label in totals |
recipient | string | Recipient name. Shown as the first line of the Ship To block |
address | object | Shipping address. See Address. |
Shipping details are shown on invoices, receipts, and quotes. For
cancellations, only the shipping amount is used in totals.
"tax": { "amount": 136.40, "label": "Sales Tax", "rate": 8.25, "breakdown": [ {"name": "TX State Tax 6.25%", "rate": 6.25, "amount": 103.44}, {"name": "Austin Local Tax 2%", "rate": 2, "amount": 32.96} ] }
| Field | Type | Description |
|---|---|---|
amount | number | Total tax amount. Omitted from document when zero |
label | string | Custom tax label. Defaults to “Tax” in document language |
rate | number | Tax rate percentage. Shown next to the tax label when no breakdown is present |
breakdown | array | Optional itemized tax rows. Supported row fields: name, rate, amount, and base. The base field is the taxable net amount for the rate; EU invoices must show the taxable amount per rate. When omitted, a single tax line is shown |
Set the document_type field to select the document template.
Each type supports the common fields above plus type-specific fields.
Set "document_type": "invoice".
Full invoice with seller and customer details, line items, tax breakdown,
discounts, shipping, bank details, and payment terms.
Starter file:
invoice.json.
| Field | Type | Description |
|---|---|---|
due_date | string | Payment due date (YYYY-MM-DD) |
supply_date | string | Date of taxable supply / delivery (YYYY-MM-DD). EU VAT invoices must state it when it differs from the issue date (Czech DUZP, German Leistungsdatum, UK tax point) |
payment_terms | string | Payment terms (e.g. “Net 30”) |
payment_instructions | string | Payment instructions |
purchase_order_number | string | PO number |
fulfillment_status | string | Auto-translated fulfillment status. Values:
fulfilled, partial, unfulfilled, restocked
|
Set "document_type": "receipt".
Payment confirmation with transaction details, line items, shipping, and tip.
Starter file:
receipt.json.
| Field | Type | Description |
|---|---|---|
payment_method | string | Auto-translated payment method. Values:
credit_card, debit_card, cash,
bank_deposit, money_order, gift_card,
exchange, manual
|
payment_status | string | Auto-translated payment status. Values:
paid, pending, authorized,
partially_paid, partially_refunded,
refunded, voided
|
fulfillment_status | string | Auto-translated fulfillment status. Values:
fulfilled, partial,
unfulfilled, restocked
|
transaction_id | string | Transaction reference |
tip_amount | number | Tip / gratuity amount |
purchase_order_number | string | PO number |
Set "document_type": "quote".
Quotation with validity date and shipping details.
Starter file:
quote.json.
| Field | Type | Description |
|---|---|---|
valid_until | string | Quote expiration date (YYYY-MM-DD) |
reference_number | string | Reference number |
payment_terms | string | Payment terms |
payment_instructions | string | Payment instructions |
purchase_order_number | string | PO number |
Set "document_type": "refund" or "credit_note".
Refund notice with reason and original invoice reference.
Starter file:
refund.json.
| Field | Type | Description |
|---|---|---|
original_invoice | string | Original invoice/order number |
original_invoice_date | string | Issue date of the original invoice (YYYY-MM-DD). Printed after the number — corrective documents conventionally reference the original by number and date |
reason | string | Refund reason (free text) |
refund_method | string | Auto-translated refund method. Values:
credit_card, debit_card, cash,
bank_deposit, money_order, gift_card,
exchange, manual
|
refund_status | string | Auto-translated refund status. Values:
paid, pending, authorized,
partially_paid, partially_refunded,
refunded, voided
|
Amounts and signs. Use positive amounts for the values being
refunded or credited. The refund or credit_note
document type provides the refund context; the API does not flip signs
automatically. Use negative values only for adjustments that should be
printed as negative amounts.
Set "document_type": "cancellation".
Order cancellation notice with reason and refund status.
Starter file:
cancellation.json.
| Field | Type | Description |
|---|---|---|
original_invoice | string | Number of the invoice issued for the cancelled order, if any |
original_invoice_date | string | Issue date of that invoice (YYYY-MM-DD); printed after the number |
related_credit_note | string | Number of the credit note issued for this cancellation, if any |
cancellation_date | string | Cancellation date (YYYY-MM-DD) |
cancellation_reason | string | Auto-translated cancellation reason. Values:
customer, fraud, inventory,
declined, staff, other
|
refund_status | string | Auto-translated refund status. Values:
paid, pending, authorized,
partially_paid, partially_refunded,
refunded, voided
|
The cancellation document is a commercial notice — it confirms that
an order was cancelled and has no tax effect. If an invoice was already
issued for the order, cancelling it requires a credit note (document type
refund/credit_note) referencing the original
invoice; the cancellation notice can accompany it via
related_credit_note. For orders cancelled before any invoice
was issued, the notice alone is sufficient.
A complete invoice payload with common invoice fields. Starter files for the other document types are linked from their sections above.
{ "document_type": "invoice", "document_number": "INV-2026-0042", "issue_date": "2026-03-15", "due_date": "2026-04-15", "currency": "USD", "purchase_order_number": "PO-9981", "payment_terms": "Net 30", "payment_instructions": "Please remit payment via ACH or check.", "notes": "Thank you for your business.", "fulfillment_status": "unfulfilled", "seller": { "name": "Acme Corp", "email": "billing@acmecorp.com", "phone": "+1 212 555 0199", "website": "https://acmecorp.com", "tax_id": "EIN 12-3456789", "logo_url": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA5NiA5NiI+PHJlY3Qgd2lkdGg9Ijk2IiBoZWlnaHQ9Ijk2IiByeD0iMjAiIGZpbGw9IiMyMTRFNjMiLz48cGF0aCBkPSJNMjQgNjIgNDggMjBsMjQgNDJINjBMNDggNDAgMzYgNjJ6IiBmaWxsPSIjRjREMzVFIi8+PHBhdGggZD0iTTMxIDcwaDM0IiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9zdmc+", "address": { "address1": "100 Market St", "address2": "Suite 300", "city": "San Francisco", "province": "CA", "zip": "94105", "country": "United States", "country_code": "US" } }, "customer": { "name": "Jane Cooper", "email": "jane@cooperdesign.com", "phone": "+1 415 555 0100", "company": "Cooper Design LLC", "address": { "address1": "456 Oak Ave", "city": "Austin", "province": "TX", "zip": "73301", "country": "United States", "country_code": "US" } }, "items": [ {"description": "Web Design Services", "quantity": 10, "unit_price": 150.00, "sku": "SVC-WEB-01"}, {"description": "Hosting (annual)", "quantity": 1, "unit_price": 240.00} ], "subtotal": 1740.00, "discount": {"amount": 100.00, "label": "Loyalty discount"}, "shipping": {"amount": 15.00, "name": "Express Shipping"}, "tax": { "amount": 136.40, "label": "Sales Tax", "breakdown": [ {"name": "TX State Tax 6.25%", "rate": 6.25, "amount": 103.44}, {"name": "Austin Local Tax 2%", "rate": 2, "amount": 32.96} ] }, "total": 1791.40 }
The API localizes the generated PDF from a small set of fields in the JSON request.
currency sets the currency symbol and provides the default
language and region.
locale overrides the language and region used for labels,
dates, numbers, status text, and region-specific identifier labels.
address.country_code controls address formatting, such as
postal-code placement.
Use ISO 3166-1 alpha-2 country codes such as US,
DE, GB, FR, CZ, or
JP for address.country_code. The code is used for
address formatting, not printed as the country name. See the
ISO 3166 country codes.
Localized output includes document labels, status values, dates, numbers, currency formatting, address layout, and locale-specific labels for tax and company identifiers.
The following currencies have full locale support with automatic language detection, labels, and date formatting. Any valid ISO 4217 currency code can be used.
| Currency | Name |
|---|---|
USD | US Dollar |
EUR | Euro |
GBP | British Pound |
CAD | Canadian Dollar |
AUD | Australian Dollar |
JPY | Japanese Yen |
CNY | Chinese Yuan |
KRW | South Korean Won |
INR | Indian Rupee |
BRL | Brazilian Real |
MXN | Mexican Peso |
CZK | Czech Koruna |
PLN | Polish Zloty |
SEK | Swedish Krona |
NOK | Norwegian Krone |
DKK | Danish Krone |
CHF | Swiss Franc |
RUB | Russian Ruble |
TRY | Turkish Lira |
THB | Thai Baht |
HUF | Hungarian Forint |
RON | Romanian Leu |
BGN | Bulgarian Lev |
HRK | Croatian Kuna |
ILS | Israeli Shekel |
SAR | Saudi Riyal |
IDR | Indonesian Rupiah |
MYR | Malaysian Ringgit |
VND | Vietnamese Dong |
TWD | Taiwan Dollar |
ZAR | South African Rand |
UAH | Ukrainian Hryvnia |
BDT | Bangladeshi Taka |
PKR | Pakistani Rupee |
Labels, status translations, and formatting are available in 50 languages.
Structured invoice JSON is the default request format. To send data straight
from an e-commerce platform instead, set the app query
parameter — the adapter maps the platform's data to the right
document automatically.
| Parameter | Default | Description |
|---|---|---|
app |
omitted | Omit for structured invoice JSON, shopify for Shopify data, or stripe for Stripe data. |
Send Shopify order data directly with ?app=shopify.
Both REST API and GraphQL formats are supported.
The appropriate document type (invoice, receipt, quote, refund, or
cancellation) is selected automatically based on the order data.
Save your Shopify order JSON to shopify_order.json, then run
the request below to save the PDF as receipt.pdf.
curl -f -s -S \ -u 'demo':'demo' \ -H 'Content-Type: application/json' \ --data-binary @shopify_order.json \ -o receipt.pdf \ 'https://api.pdfcrowd.com/templates/business/?app=shopify'
import requests with open('shopify_order.json', 'rb') as invoice_file: response = requests.post( 'https://api.pdfcrowd.com/templates/business/?app=shopify', auth=('demo', 'demo'), headers={'Content-Type': 'application/json'}, data=invoice_file, allow_redirects=False, ) response.raise_for_status() with open('receipt.pdf', 'wb') as output_file: output_file.write(response.content)
import { readFile, writeFile } from 'node:fs/promises'; const username = 'demo'; const apiKey = 'demo'; const response = await fetch("https://api.pdfcrowd.com/templates/business/?app=shopify", { method: 'POST', headers: { Authorization: 'Basic ' + Buffer.from(username + ':' + apiKey).toString('base64'), 'Content-Type': 'application/json', }, body: await readFile("shopify_order.json"), redirect: 'manual', }); if (!response.ok) { throw new Error('HTTP ' + response.status + ' ' + response.statusText); } await writeFile("receipt.pdf", Buffer.from(await response.arrayBuffer()));
import axios from 'axios'; import { readFile, writeFile } from 'node:fs/promises'; const username = 'demo'; const apiKey = 'demo'; try { const response = await axios.post( "https://api.pdfcrowd.com/templates/business/?app=shopify", await readFile("shopify_order.json"), { auth: { username, password: apiKey }, headers: { 'Content-Type': 'application/json' }, responseType: 'arraybuffer', maxRedirects: 0, }, ); await writeFile("receipt.pdf", response.data); } catch (error) { console.error(error.message); process.exitCode = 1; }
<?php require 'vendor/autoload.php'; use GuzzleHttp\Client; use GuzzleHttp\Psr7\Utils; try { $client = new Client(); $response = $client->post('https://api.pdfcrowd.com/templates/business/?app=shopify', [ 'auth' => ['demo', 'demo'], 'headers' => ['Content-Type' => 'application/json'], 'body' => Utils::tryFopen('shopify_order.json', 'rb'), 'allow_redirects' => false, 'http_errors' => false, ]); $status = $response->getStatusCode(); if ($status < 200 || $status >= 300) { throw new \RuntimeException("HTTP $status " . $response->getReasonPhrase()); } $body = (string) $response->getBody(); if (file_put_contents('receipt.pdf', $body) !== strlen($body)) { throw new \RuntimeException('Failed to write output file'); } } catch (\Throwable $error) { fwrite(STDERR, $error->getMessage() . "\n"); exit(1); }
Shopify orders don't include seller details — pass your company
information via query parameters such as
seller_name, seller_email, logo_url, etc.
The document language and formatting are automatically detected from the customer's locale or the order currency. Status values (e.g. paid, fulfilled) are translated to the document language.
Send Stripe data directly with ?app=stripe.
Both raw Stripe API responses (Invoice, Quote, Refund) and full webhook
event payloads are supported.
The appropriate document type (invoice, receipt, quote, refund, or
cancellation) is selected automatically based on the data.
| Stripe Event | Document |
|---|---|
invoice.finalized | Invoice |
invoice.payment_succeeded | Receipt |
invoice.voided | Cancellation |
quote.finalized | Quote |
refund.created | Credit Note |
Save your Stripe event JSON to stripe_event.json, then run
the request below to save the PDF as invoice.pdf.
curl -f -s -S \ -u 'demo':'demo' \ -H 'Content-Type: application/json' \ --data-binary @stripe_event.json \ -o invoice.pdf \ 'https://api.pdfcrowd.com/templates/business/?app=stripe'
import requests with open('stripe_event.json', 'rb') as invoice_file: response = requests.post( 'https://api.pdfcrowd.com/templates/business/?app=stripe', auth=('demo', 'demo'), headers={'Content-Type': 'application/json'}, data=invoice_file, allow_redirects=False, ) response.raise_for_status() with open('invoice.pdf', 'wb') as output_file: output_file.write(response.content)
import { readFile, writeFile } from 'node:fs/promises'; const username = 'demo'; const apiKey = 'demo'; const response = await fetch("https://api.pdfcrowd.com/templates/business/?app=stripe", { method: 'POST', headers: { Authorization: 'Basic ' + Buffer.from(username + ':' + apiKey).toString('base64'), 'Content-Type': 'application/json', }, body: await readFile("stripe_event.json"), redirect: 'manual', }); if (!response.ok) { throw new Error('HTTP ' + response.status + ' ' + response.statusText); } await writeFile("invoice.pdf", Buffer.from(await response.arrayBuffer()));
import axios from 'axios'; import { readFile, writeFile } from 'node:fs/promises'; const username = 'demo'; const apiKey = 'demo'; try { const response = await axios.post( "https://api.pdfcrowd.com/templates/business/?app=stripe", await readFile("stripe_event.json"), { auth: { username, password: apiKey }, headers: { 'Content-Type': 'application/json' }, responseType: 'arraybuffer', maxRedirects: 0, }, ); await writeFile("invoice.pdf", response.data); } catch (error) { console.error(error.message); process.exitCode = 1; }
<?php require 'vendor/autoload.php'; use GuzzleHttp\Client; use GuzzleHttp\Psr7\Utils; try { $client = new Client(); $response = $client->post('https://api.pdfcrowd.com/templates/business/?app=stripe', [ 'auth' => ['demo', 'demo'], 'headers' => ['Content-Type' => 'application/json'], 'body' => Utils::tryFopen('stripe_event.json', 'rb'), 'allow_redirects' => false, 'http_errors' => false, ]); $status = $response->getStatusCode(); if ($status < 200 || $status >= 300) { throw new \RuntimeException("HTTP $status " . $response->getReasonPhrase()); } $body = (string) $response->getBody(); if (file_put_contents('invoice.pdf', $body) !== strlen($body)) { throw new \RuntimeException('Failed to write output file'); } } catch (\Throwable $error) { fwrite(STDERR, $error->getMessage() . "\n"); exit(1); }
Stripe invoices auto-populate seller_name from the invoice's
account_name field. Other seller details — email,
phone, address, logo, tax ID — are not available in Stripe and
must be passed via query parameters such as
seller_email, logo_url, etc.
The document language and formatting are automatically detected from the
invoice currency (e.g. eur → German, czk
→ Czech). Pass &locale=fr_FR to override.
Stripe webhooks deliver related objects (customer,
charge, payment_intent, …) as bare ID
strings rather than full objects. When posting a raw webhook payload
directly — with no middleware in between — expand the
objects you want rendered:
customer (otherwise the buyer block is empty).charge and charge.invoice to render the originating invoice number and line items.See Stripe's expanding objects reference. Integrations that go through a middleware layer (e.g. Zapier's Stripe trigger) usually receive already-expanded payloads and need no extra handling.
400405POST requests are accepted.401403429430500If your system already has invoice data in another format, ask an AI coding assistant to create an adapter that maps it to the structured invoice JSON format.
For example, the source can be JSON, XML, CSV, a database record, or an existing invoice object. Tell the assistant which language or framework you use.
Create an adapter for my application that: 1. Reads invoice data from [DESCRIBE THE SOURCE OR PASTE A SAMPLE] 2. Maps it to the PDFCrowd Invoice PDF API JSON format: https://pdfcrowd.com/invoice-pdf-api/docs/ 3. Sends the mapped JSON to the Invoice PDF API endpoint and saves the PDF Use [LANGUAGE / FRAMEWORK].