· Dan · Guides · 4 min read
Put Anything on a ConnectWise Invoice with n8n
Use n8n to pull data from any API onto your ConnectWise invoices via Better Invoice. Includes a ready-to-use workflow for showing workstations and users.
Better Invoice supports custom data sources. You give it a webhook URL, it calls it when generating each invoice, and whatever JSON comes back is available in your template. That means anything you can query from an API, you can display on an invoice.
Think about what would be useful for your clients to see every month:
- Workstations and assigned users
- Backup status
- Patch compliance numbers
- Tickets resolved this billing cycle
- Active licenses or products
- Contact directories
The pattern is always the same: build an n8n workflow that returns JSON from a webhook, point Better Invoice at it, and render the data in your template. Swap the API call, change the fields, and you’re showing something completely different.
Below is one example that pulls workstation and user data from ConnectWise configurations. Clients ask for this all the time, so instead of pulling a report every time, just put it on the invoice.
The Workflow
This workflow creates a webhook endpoint that Better Invoice calls when generating an invoice. It receives a company ID, queries ConnectWise for active configurations, and returns a clean list of workstations and their assigned users.
How It Works
-
Webhook receives a request from Better Invoice with the company ID as a query parameter (
?companyid=408). -
Get Configurations queries ConnectWise for all active configurations belonging to that company, returning the device name and assigned contact.
-
Edit Fields maps the response into a clean two-column format:
userandworkstation. -
Sort orders the results alphabetically by user name so the list is easy to scan.
-
Respond to Webhook sends the sorted JSON array back to Better Invoice to render on the invoice.
Setting It Up
1. Deploy the Workflow
Import the workflow into your n8n instance and activate it. Note the webhook URL, you’ll need it in the next step.
2. Add It as a Data Source in Better Invoice
Follow the Better Invoice guide on adding n8n data sources to ConnectWise invoices to register your webhook URL as a custom data source.
Your endpoint URL will look something like:
https://your-n8n-instance.com/webhook/your-path?companyid={{ company.id }} The {{ company.id }} token gets replaced automatically by Better Invoice with the ConnectWise company ID for each invoice.
3. Add It to Your Invoice Template
Once the data source is configured, you can use it in the Better Invoice template editor. Better Invoice has a full-featured templating engine with support for icons and markdown, so you can display the data however you want.
You don’t even need to write HTML. Your workflow could return straight markdown from a field in IT Glue or your documentation platform, and Better Invoice will render it on the invoice.
Here’s one example using the templating engine to render a Users section and a Workstations section:
{% if sources.workstations and sources.workstations.size > 0 %}
<div class="mt-4 mb-4">
{% comment %} Build unique user list {% endcomment %}
{% assign seenUsers = "" %}
{% for ws in sources.workstations %}
{% unless ws.user == blank %}
{% unless seenUsers contains ws.user %}
{% assign seenUsers = seenUsers | append: ws.user | append: "|" %}
{% endunless %}
{% endunless %}
{% endfor %}
{% assign uniqueUsers = seenUsers | split: "|" %}
<p class="text-xs font-semibold uppercase tracking-wide mb-2"
style="color: var(--secondary);">
Users
</p>
<div style="display: flex; flex-wrap: wrap; column-gap: 0.75rem;
font-size: 0.75rem; line-height: {{ config.typography.lineHeight }};
margin-bottom: 1rem;">
{% for user in uniqueUsers %}
{% unless user == blank %}
<span style="white-space: nowrap;">
{% icon "ph-user" %} {{ user }}
</span>
{% endunless %}
{% endfor %}
</div>
<p class="text-xs font-semibold uppercase tracking-wide mb-2"
style="color: var(--secondary);">
Workstations
</p>
<div style="display: flex; flex-wrap: wrap; column-gap: 0.75rem;
font-size: 0.75rem; line-height: {{ config.typography.lineHeight }};">
{% for ws in sources.workstations %}
{% unless ws.workstation == blank %}
<span style="white-space: nowrap;">
{% icon "ph-desktop-tower" %} {{ ws.workstation }}
</span>
{% endunless %}
{% endfor %}
</div>
</div>
{% endif %} Other Ideas
This workflow queries /company/configurations, but the same pattern works with any ConnectWise endpoint:
/service/ticketsfor recent tickets resolved for this client/procurement/productsfor active licenses and products/company/contactsfor a full contact directory
You could also pull from IT Glue, Hudu, or any other API your n8n instance can reach.