Funnel chart
Stages from top to bottom, each as wide as its value, with the share that made it from the stage before.
Who this is for
People who build n8n workflows, and agents that send a conversion report to Slack or email: visitors to signups to customers, leads to deals, applications to hires. You have one number per stage.
What the funnel preset draws
- One centred bar for each stage, from the first to the last, top to bottom. The width of a bar is proportional to its value, and the widest bar fills the chart.
- The value of each stage on the right.
- Next to it, the share of the previous stage, so 3,100 signups out of 12,400 visitors reads
25%. The first stage has none.
Send a bar config with one dataset: data is the value of each stage and labels their names. Up to 12 stages, none of them negative. A stage that is not a number has no bar and shows a dash. A stage after an empty one has no share.
The config
{
"type": "bar",
"data": {
"labels": ["Visitors", "Signups", "Trials", "Paying"],
"datasets": [{ "data": [12400, 3100, 930, 214] }]
},
"options": {
"plugins": {
"title": { "display": true, "text": "Signup funnel, October" },
"chartpng": { "preset": "funnel" }
}
}
}The preset and its options live under options.plugins.chartpng:
| Option | What it does |
|---|---|
preset | "funnel". |
prefix | A string put before each value, such as "$". |
suffix | A string put after each value, such as " users". |
values | false hides the values and the shares and leaves the bars. |
Anything your config sets wins over the preset, the same as with the theme. The bar colour is the first colour of the theme, or the backgroundColor of the dataset.
A config that does not fit is a 400 whose message says why, such as preset funnel needs type bar or preset funnel needs stage values of zero or more; stage 2 is negative.
The GET URL
The whole config goes in c, URL-encoded. Paste the URL in an <img>, an email or a Slack image block. Past a few thousand characters, use a POST.
https://chartpng.dev/chart?c=%7B%22type%22%3A%22bar%22%2C%22data%22%3A%7B%22labels%22%3A%5B%22Visitors%22%2C%22Signups%22%2C%22Trials%22%2C%22Paying%22%5D%2C%22datasets%22%3A%5B%7B%22data%22%3A%5B12400%2C3100%2C930%2C214%5D%7D%5D%7D%2C%22options%22%3A%7B%22plugins%22%3A%7B%22title%22%3A%7B%22display%22%3Atrue%2C%22text%22%3A%22Signup%20funnel%2C%20October%22%7D%2C%22chartpng%22%3A%7B%22preset%22%3A%22funnel%22%7D%7D%7D%7D&w=800&h=450&dpr=2The same chart with a POST
A POST takes the same fields in a JSON body. Use it from a server, or whenever the URL gets too long. The response is the PNG.
curl https://chartpng.dev/chart \
-H "Content-Type: application/json" \
-d '{"c": {"type":"bar","data":{"labels":["Visitors","Signups","Trials","Paying"],"datasets":[{"data":[12400,3100,930,214]}]},"options":{"plugins":{"title":{"display":true,"text":"Signup funnel, October"},"chartpng":{"preset":"funnel"}}}}, "w": 800, "h": 450, "dpr": 2}' \
-o funnel-chart.pngIn n8n
Build the config in a Code node and call https://chartpng.dev/chart with the HTTP Request node, as then8n guide shows. The templateweekly KPI report to Slack builds a KPI and a comparison and posts both images in one Slack message.
If a model wrote the config
Models often send near-JSON: a code fence around it, single quotes, a trailing comma. ChartPNG repairs the usual cases and lists what it changed in the X-ChartPNG-Fixes response header. Functions written as text are removed, never run. See lenient parsing.