Countdown timers in n8n
n8n has a Wait node, and for a short pause inside one execution it is the right tool. For a deadline measured in days — a trial, an invitation, a hold — you want something that outlives the execution and can be shown to a customer. This guide wires the Countdown Timer API into n8n with the HTTP Request node, a Webhook trigger, and one Code node for signature verification. No community node required.
Wait node versus a timer
| Wait node | Countdown timer | |
|---|---|---|
| Pause inside one execution | Yes | Not applicable |
| Survives an n8n restart | Depends on configuration and duration | Yes — the deadline lives outside n8n |
| Survives editing the workflow | Pending executions can be affected | Yes |
| Visible to a customer | No | Hosted page, embed, and email GIF |
| Deadline can be moved after the fact | No | PATCH the timer |
| Milestones before the end | A second Wait branch | A second rule on the same timer |
| Best for | Short waits, rate limiting, polling gaps | Deadlines measured in hours or days |
Setting up the credential
Once, and every HTTP Request node afterwards can reference it.
Header Auth credential
Credentials -> New -> Header Auth
Name: CountdownShare
Name: Authorization
Value: Bearer cs_live_your_key_here
Using a credential rather than a hardcoded header keeps the key
out of the workflow JSON — which matters as soon as you export
a workflow or commit it to version control.cs_test_ key. Sandbox is free, behaves identically, and means a workflow you are still building cannot publish anything or consume your Production allowance.Creating a timer
HTTP Request node
HTTP Request node — "Create countdown"
Method: POST
URL: https://countdownshare.com/api/v1/timers
Authentication: Generic -> Header Auth -> CountdownShare
Headers:
Idempotency-Key {{ "trial_" + $json.customer_id }}
Body (JSON):
{
"name": "Trial — {{ $json.email }}",
"type": "personalized",
"duration_seconds": 1209600,
"external_user_id": "{{ $json.customer_id }}",
"publish": true
}The Idempotency-Key expression
{{ "trial_" + $json.customer_id }} derives the key from the incoming data rather than generating a random one. n8n retries failed nodes, and without a stable key a retry creates a second timer — quietly granting someone a twenty-eight day trial. This is the single most important detail on the page.
The response contains data.id. Store it against your record in whatever comes next — Airtable, Postgres, a CRM — because every later call takes it.
Attaching a rule
Register a webhook destination once, by hand, pointing at your n8n Webhook trigger URL. Then each workflow attaches rules to its own timers.
HTTP Request node
HTTP Request node — "Notify me when it ends"
Method: POST
URL: https://countdownshare.com/api/v1/timers/{{ $json.data.id }}/webhook-rules
Body (JSON):
{
"name": "Trial ended",
"webhook_destination_id": "{{ $env.CDS_DESTINATION_ID }}",
"condition": {
"field": "status",
"operator": "equals",
"value": "ended"
},
"delivery": "once"
}
Register the destination once, by hand, pointing at your n8n
Webhook trigger URL. Reuse its ID across every workflow.Rules are unlimited on every plan, so a reminder ladder is just more of these — one at remaining_seconds <= 86400, one at 3600, one on ended. Each delivery carries its own rule.name, which is what you branch on. See webhook rules.
Receiving the callback
A Webhook trigger node receives the delivery. One setting matters: turn on Raw Body, or the signature cannot be verified.
Code node — verify before acting
// Code node, immediately after the Webhook trigger.
// Set the Webhook node's "Raw Body" option to ON, or the
// signature can never be verified.
const crypto = require('crypto');
const secret = $env.CDS_WEBHOOK_SECRET;
const raw = $input.first().binary
? Buffer.from($input.first().binary.data.data, 'base64').toString()
: $input.first().json.body;
const headers = $input.first().json.headers;
const timestamp = headers['x-countdownshare-timestamp'];
const signature = headers['x-countdownshare-signature'];
if (!timestamp || !signature) {
throw new Error('Unsigned delivery');
}
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
throw new Error('Stale delivery');
}
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${raw}`)
.digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(signature.replace(/^v1=/, ''), 'hex');
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
throw new Error('Invalid signature');
}
return [{ json: JSON.parse(raw) }];Your webhook URL is public. Without verification, anyone who discovers it can trigger the workflow with a payload they wrote — which, in a workflow that cancels orders or downgrades accounts, is a real problem.
Three workflows worth building
Trial expiry
Signup webhook → create a personalized timer → rule on ended → downgrade in your billing tool and send a win-back email.
Abandoned cart
Cart event → duration timer → rule at 1 hour remaining → send a reminder with the email GIF → rule on ended → release stock.
Recurring cutoff
One recurring timer at 15:00 daily → repeat rule → post the day’s order summary to Slack before the cutoff.
The third one needs delivery: "repeat" rather than the default. A once rule on a recurring timer fires on the first cycle and never again, which is the most common “my automation stopped working” report.
Common questions
Why not just use the n8n Wait node?
The Wait node is fine for short waits inside a single execution. For long ones it keeps an execution open, which ties the deadline to that execution surviving — a restart, an upgrade, or a workflow edit can lose it. It also gives you nothing to show a customer. A timer is a record that outlives any particular execution.
Is there an official n8n community node?
No. The HTTP Request node handles everything, since the API is plain REST with bearer auth. Setting up Header Auth credentials once means every node afterwards is a URL and a body.
Why does my signature verification fail in n8n?
The Webhook node parses JSON by default, and a re-serialised object is not the bytes that were signed. Turn on the Raw Body option in the Webhook node settings so the original body reaches your Code node intact.
Can one webhook destination serve several workflows?
Yes, and it should. Destinations are limited by plan while rules are unlimited. Point one destination at a single n8n webhook, then branch inside the workflow on rule.name or timer.metadata to route to the right logic.
Next steps
Nothing to install
The HTTP Request node is all you need. Sandbox is free with any account — build the workflow against a test key first.