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 nodeCountdown timer
Pause inside one executionYesNot applicable
Survives an n8n restartDepends on configuration and durationYes — the deadline lives outside n8n
Survives editing the workflowPending executions can be affectedYes
Visible to a customerNoHosted page, embed, and email GIF
Deadline can be moved after the factNoPATCH the timer
Milestones before the endA second Wait branchA second rule on the same timer
Best forShort waits, rate limiting, polling gapsDeadlines measured in hours or days
The row that decides it is usually the last one. A Wait node holds an execution open; that is fine for thirty seconds and increasingly awkward for fourteen days, because anything that disturbs n8n in that fortnight can disturb the deadline.

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.
Start with a 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.

Throwing inside the Code node fails the execution, which returns a 5xx and causes the delivery to be retried. For a genuinely invalid signature that is not ideal — you would rather reject permanently — but it is safe, and an n8n error notification tells you something is wrong. See retries and replay.

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.