Create Custom Invoices with Word Templates and Foxit Document Generation
Invoicing is a critical part of any business, often involving multiple steps—gathering customer data, calculating amounts owed, and sending out invoices so your company can get paid. Foxit's Document Generation API streamlines this process by making it easy to create well-formatted, dynamic PDF invoices. Let's walk through an example.
Before You Start
If you want to follow along with this blog post, be sure to get your free credentials over on our developer portal. Also, read our introductory blog post, which covers the basics of working with our API.
As a reminder, the API makes use of Microsoft Word templates. These templates are essentials tokens wrapped in double brackets. When you call the API, you'll pass the template and your data. Our API then dynamically replaces those tokens with your data and returns you a nice PDF (you can also get a Word file back as well).
Creating Your Custom Invoice with Word Templates
Let's begin by designing the template in Word. An invoice typically includes things like:
- The customer receiving the invoice
- The invoice number and issue date
- The payment due date
- A detailed list of items, including name, quantity, and price for each line item, with a total at the end
The Document Generation API makes no requirements in terms of how you design your templates. Size, alignment, and so forth, can match your corporate styles and be as fancy, or simple, as you like. Let's consider the template below (I'll link to where you can download this file at the end of the article):

Let's break it down from the top.
- The first token,
{{ invoiceNum }}
, represents the invoice number for the customer. - The next token is special.
{{ today \@ MM/dd/yyyy }}
represents two different features of the Document Generation API. First,today
is a special value representing the present time, or more accurately, when you call the API. The next portion represents a date mask for representing a date value. Our docs have a list of available masks. {{ accountName }}
is another regular token.- The payment date,
{{ paymentDueDate \@ MM/dd/yyyy }}
, shows how the date mask feature can be used on dates in your own data as well. - Now let's look at the table. You can format tables however you like, but a common setup includes one row for the header and one row for the dynamic data. (In this example, there’s also a third row, which I'll explain shortly.) To start, you’ll use a marker tag:
{{TableStart:lineItems}}
, wherelineItems
represents an array in your data. The row ends with the matching{{TableEnd:lineItems}}
tag. Between these two tags, you'll place additional tags for each value in the array. For example, we have aproduct
,qty
,price
, andtotalPrice
for each item. You'll also see the specialROW_NUMBER
value, which automatically counts each row starting at 1. Finally, the\# Currency
format is applied to thetotalPrice
value to display it as a currency. - The last row in the table uses two special features together, namely
SUM(ABOVE)
, which maps to creating a total of the last column from the table. This can be paired with currency formatting as shown.
Alright, now that you've seen the template, let's talk data!
The Data for Your Custom Invoices
Usually the data for an operation like this would come from a database, or perhaps an API with an ecommerce system. For this demo, the data will come from a simple JSON file. Let's take a look at it:
[
{
"invoiceNum":100,
"accountName":"Customer Alpha",
"accountNumber":1,
"paymentDueDate":"August 15, 2025",
"lineItems":[
{"product":"Product 1", "qty":5, "price":2, "totalPrice":10},
{"product":"Product 5", "qty":3, "price":9, "totalPrice":18},
{"product":"Product 4", "qty":1, "price":50, "totalPrice":50},
{"product":"Product X", "qty":2, "price":15, "totalPrice":30}
]
},
{
"invoiceNum":25,
"accountName":"Customer Beta",
"accountNumber":2,
"paymentDueDate":"August 15, 2025",
"lineItems":[
{"product":"Product 2", "qty":9, "price":2, "totalPrice":18},
{"product":"Product 4", "qty":1, "price":8, "totalPrice":8},
{"product":"Product 3", "qty":10, "price":25, "totalPrice":250},
{"product":"Product YY", "qty":3, "price":15, "totalPrice":45},
{"product":"Product AA", "qty":2, "price":100, "totalPrice":200}
]
},
{
"invoiceNum":51,
"accountName":"Customer Gamma",
"accountNumber":3,
"paymentDueDate":"August 15, 2025",
"lineItems":[
{"product":"Product 9", "qty":1, "price":2, "totalPrice":2},
{"product":"Product 23", "qty":30, "price":9, "totalPrice":270},
{"product":"Product ZZ", "qty":6, "price":15, "totalPrice":90}
]
}
]
The data consists of an array of 3 sets of invoice data. Each set follows the same pattern and matches what you saw above in the Word template. The only exception being the accountNumber
value which wasn't used in the template. That's fine – sometimes your data will include things not necessary for the final PDF. In this case, though, we're actually going to make use of it (you'll see in a moment). Onward to code!
Calling the Foxit API with Our Data
Now for my favorite part – actually calling the API. The Generate Document API is incredibly simple; needing just your credentials, a base64 version of the template, and your data. The entire demo is slightly over 50 lines of Python code, so let's look at the template and then break it down.
import os
import requests
import sys
from time import sleep
import base64
import json
from datetime import datetime
CLIENT_ID = os.environ.get('CLIENT_ID')
CLIENT_SECRET = os.environ.get('CLIENT_SECRET')
HOST = os.environ.get('HOST')
def docGen(doc, data, id, secret):
headers = {
"client_id":id,
"client_secret":secret
}
body = {
"outputFormat":"pdf",
"documentValues": data,
"base64FileString":doc
}
request = requests.post(f"{HOST}/document-generation/api/GenerateDocumentBase64", json=body, headers=headers)
return request.json()
with open('invoice.docx', 'rb') as file:
bd = file.read()
b64 = base64.b64encode(bd).decode('utf-8')
with open('invoicedata.json', 'r') as file:
data = json.load(file)
for invoiceData in data:
result = docGen(b64, invoiceData, CLIENT_ID, CLIENT_SECRET)
if result["base64FileString"] == None:
print("Something went wrong.")
print(result)
sys.exit()
b64_bytes = result["base64FileString"].encode('ascii')
binary_data = base64.b64decode(b64_bytes)
filename = f"invoice_account_{invoiceData["accountNumber"]}.pdf"
with open(filename, 'wb') as file:
file.write(binary_data)
print(f"Done and stored to {filename}")
After importing the necessary modules and loading credentials from the environment, we define a simple docGen
method. This method takes the template, data, and credentials, then calls the API endpoint. The API responds with the rendered PDF in Base64 format, which the method returns.
The main code of the template breaks down to:
- Reading in the template and converting it to base64.
- Reading in the JSON file
- Iterating over each block of invoice data and calling the API
- Remember how I said
accountNumber
wasn't used in the template? We actually use it here to generate a unique filename. Technically, you don't need to store the results at all. You could take the raw binary data and email it. But having a copy of the results does mean you can re-use it later, such as if the customer is late to pay.
Here's an example of one of the results:

Next Steps
If you want to try this demo yourself, first grab yourself a shiny free set of credentials and then head over to our GitHub to grab the template, Python, and sample output values yourself.