How to Generate Custom PDF Documents from Odoo (Step-by-Step, No QWeb)
If you have ever tried to change more than a logo on an Odoo quote or invoice, you know the wall: Odoo's documents are generated by QWeb, and structural changes mean XML, a custom module, and a developer. We compared all six ways around that wall in our custom quote designer guide. This tutorial is the hands-on follow-up for the route that removes the developer entirely.
By the end you will have installed the free PDF Generator API module from the Odoo App Store, connected your account, checked how your Odoo fields map to template placeholders, and generated your first custom PDF straight from a sales order. Total time is about 15 minutes, and none of it touches QWeb or XML.
Everything below was run on Odoo 19 with connector version 19.0.7.3.5.
Before you start
Three things to check:
- Your Odoo hosting. The module runs on Odoo.sh, on-premise, and Community, with builds for Odoo 17, 18, and 19. It does not run on Odoo Online (the SaaS tier), because Odoo Online does not allow third party modules.
- A PDF Generator API account. Sign up for the free 14 day trial; no credit card needed. Paid plans start at 8 euro per month after that.
Step 1: Install the module from the Odoo App Store
Open the Apps menu and search for pdfgeneratorapi. Search by that technical name rather than by "PDF Generator API", because the listing title is the descriptive one: "Create Branded PDFs from any record: invoices, quotes, orders and more."

Click Activate on the card. If your database does not show the module in the Apps list, download it from apps.odoo.com and add it to your addons path first, then use Update Apps List and search again.
You can confirm what you installed from the card's three-dot menu under Module Info: publisher PDF Generator API, free, LGPL-3.

Installing adds a PDF Generator API app to your main menu with four sections: Settings, Field Datasets, Template Editor, and Async Jobs.
Step 2: Connect your PDF Generator API account
Go to PDF Generator API > Settings. The Credentials block at the top asks for three values, all from your pdfgeneratorapi.com account:
- API Key, your public API key.
- API Secret, which signs the JWT tokens. This one stays confidential and the field masks it.
- Workspace Identifier, which is your account email for a regular workspace, or the sub-workspace identifier if you use those.

Click Test Connection. A confirmation appears naming the workspace you reached, for example "Connected to workspace: yourname@company.com". If it fails, the key and secret are the usual culprits, so re-copy both from the dashboard rather than retyping them.
Two settings blocks further down are worth knowing about, though most people never touch them:
- Advanced Settings lets you override the API endpoint. The default is us1.pdfgeneratorapi.com. Set the optional Editor Web URL only when Odoo reaches the API through a hostname your browser cannot resolve, such as a Docker service name or a private VPC endpoint, or when your company runs a dedicated deployment on its own domain.
- Attachments controls what happens when someone clicks Generate twice on the same record. Keep all versions adds a new attachment each time. Replace deletes the previous PDF API documents on that record first. Manually uploaded PDFs are never touched either way.
Step 3: Turn on the document types you need
Still in Settings, the Document types block decides where the button shows up. Nothing appears on a record until you enable its type. There are six:
- Invoices and credit notes, covering customer invoices, vendor bills, and credit notes
- Quotations and sale orders
- Purchase orders
- Delivery slips and goods receipts
- Manufacturing orders and work orders
- Rental orders, including contracts and pickup or return slips
Each one you enable does two things: it adds a Generate custom PDF button to that model's records, and it creates a starter field dataset you can customise. Enabling rental orders also switches on the sales bridge automatically.
Save when you are done.
Step 4: Check how your Odoo fields map to the template
This is the part people expect to find in the editor, and it is not there. It has its own screen: PDF Generator API > Field Datasets.
Enabling document types in Step 3 created a starter dataset for each one. On a standard setup you get five: Invoice, Manufacturing Order, Purchase Order, Quotation / Sale Order, and Stock Transfer, each bound to its Odoo model.

Open Quotation / Sale Order and you can see the whole mechanism. The Field Mappings table has one row per placeholder, with four columns: Placeholder Path, Is List, Expression, and Odoo Field.

The starter dataset arrives with 34 mappings already done, so you are editing rather than building from nothing. Placeholder paths use dot notation and group naturally:
- Order level:
order_numbertoname,order_datetodate_order,client_referencetoclient_order_ref - Customer: customer.name, customer.email,
customer.street,customer.city,customer.zip,customer.country_code - Company: company.name, company.email,
company.phone,company.vat - Totals:
totals.untaxed,totals.tax,totals.total - Line items:
lines, the one row with Is List switched on, bound toorder_line
To change a mapping, use the field palette on the right. It lists every field on the underlying model with its technical name and type, has a filter box at the top, and drills into relational fields so you can walk from partner_id down to the exact value you want. Drag a field from the palette onto a row to bind it. The Expression column is there when a raw field is not enough and you need a computed value.
Two buttons on this screen save real time:
- Preview renders the dataset against a live Odoo record, so you can see the actual values that will reach the template.
- Check coverage compares a template against the dataset and tells you exactly what does not line up. Pick a template, click Check, and you get a count plus two lists: placeholders the template asks for that your dataset does not map, and mappings your dataset provides that the template never uses.

In our run the report came back "26 / 27 placeholders covered", with one missing item, lines[].image, and seven unused mappings. That is the fastest way to find a broken document before a customer does, and it is worth running any time you edit either side.
Step 5: Design the template
Open PDF Generator API > Template Editor. Pick a template from the dropdown and the dataset to design against, and the drag-and-drop editor opens inside Odoo. If you have no templates yet, create one here.

Because you chose a dataset, the editor knows your Odoo fields. You drop a component on the page and insert the data field it should carry, which is where the placeholder paths from Step 4 show up. You can also reach the editor straight from a dataset using its Open in Editor button, which is usually the faster route when you are iterating on mapping and design together.
One practical tip carried over from support: keep embedded images small, downscaled to print size, and use JPEG for photos. Oversized images slow saving down and can time out on very large templates.
Step 6: Generate your first PDF
Open any sales order. Generate custom PDF sits right in the toolbar.

There are two paths, and the difference matters:
- The main button generates the PDF and attaches it to the record.
- The caret next to it offers Generate and Download, which sends the file straight to your browser without touching the record.

Click the main button and a dialog opens showing the document and a template picker, with your templates grouped under My Templates. If you set a Default template on the dataset back in Step 4, it is already filled in and you can just confirm.

Click Generate. A few seconds later the PDF is posted to the record's chatter with a thumbnail preview and the file named after the record, so there is a full audit trail of what was produced and when.

That is the loop closed: Odoo record in, branded PDF out, no QWeb touched.
Step 7 (optional): Email it without leaving Odoo
Click Send on the same record. The wizard attaches the generated PDF and adds two controls of its own: a Use PDF API document toggle, and a PDF API Template selector. Below them sits a live preview of the actual PDF, paged, so you can check the document before it goes out.

Turn the toggle off and Odoo falls back to its standard QWeb report, which is a useful escape hatch while you are still building confidence in a new template.
Generating for many records at once
Select several records in any list view and two buttons appear in the control panel: Generate custom PDF and View Async Jobs.

Batch runs are handled asynchronously. pdfgeneratorapi.com calls back into your Odoo when each job finishes, which is why the multi-record flow needs the Async webhook block in Settings filled in. Set the Webhook Base URL to the public origin of your Odoo, or leave it blank to fall back to the web.base.url system parameter. The Webhook Secret is generated for you on first save, and you only rotate it if you think it leaked.
Track progress under PDF Generator API > Async Jobs, which lists each job with its template, source, state, and completion time.
Beyond the sales order
The same setup covers the whole document set: customer invoices, vendor bills, credit notes, quotations, purchase orders, delivery slips and receipts, manufacturing orders, and rental orders. Design once, map fields once, and every document type gets the same treatment.
If you are still deciding whether this route fits you, the six options comparison covers the native tools too, and the Odoo integration page has the full feature rundown.
Wrapping up
That is the whole loop: install, connect, enable, map, design, generate. No QWeb, no XML, no module deployments when the layout changes. Next time someone wants the totals block moved, it is a drag in the editor, not a developer ticket.
Want a guided version with your own documents? Start the free 14 day trial or book a call with Marian Danas and bring a real Odoo quote or invoice.
Updated on: 13/08/2026
Thank you!
