Self-hosted n8n? Install the Webhook Scheduler node
The community node n8n-nodes-webhookscheduler adds Schedule, Get, Get Many, Cancel and Cancel by Reference operations with a Webhook Scheduler credential, so you skip the headers and the JSON. In n8n: Settings → Community nodes → Install → n8n-nodes-webhookscheduler. On n8n Cloud it becomes available once n8n has verified it; until then, the HTTP Request steps below work everywhere.
- Create a new workflow, for example
Invoice follow-up (runs later). - Add a Webhook trigger node. Set HTTP Method to
POST. - Open the Production URL tab and copy the URL. Do not use the Test URL: it only answers while the editor is listening.
- Save, then activate the workflow (the Active toggle, or Publish in newer versions). The Production URL only answers while the workflow is active.
When it worked: the workflow is active and you have a URL likehttps://your-name.app.n8n.cloud/webhook/….Self-hosted n8n
The URL must be public HTTPS on the default port 443, with a domain name.
localhost, an IP address, or a port such as:5678are refused. Put n8n behind a domain with HTTPS first. All rules: Target URL rules.- Create a new workflow, for example
- Create a free account (or sign in).
- Open Dashboard → API keys, name the key
n8n, and click Create key. - Copy the key. It starts with
wh_live_and is shown only once.
When it worked: the page shows Copy your key now.Open the workflow that should start the wait, or a new one with a manual trigger for the test. Add an HTTP Request node and fill it in:
HTTP Request
- Method
POST- URL
https://webhookscheduler.com/api/v1/schedule- Authentication
None- Send Headers
On- Header → Name
Authorization- Header → Value
Bearer wh_live_YOUR_KEY, with your key in place ofwh_live_YOUR_KEY. Keep the word Bearer and the space.- Send Body
On- Body Content Type
JSON- Specify Body
Using JSON- JSON
- The JSON below.
JSONjson{ "url": "https://YOUR_N8N_HOST/webhook/YOUR_PATH", "runAt": "{{ $now.plus({ minutes: 2 }).toISO() }}", "reference": "invoice:test-123", "body": { "invoiceId": "test-123", "email": "test@example.com" } }url: replace the example with workflow 2’s Production URL.runAt: two minutes from now, for the test. n8n computes it when the node runs. The JSON field must be in Expression mode (the Fixed / Expression switch above it): in Fixed mode the{{ }}part is sent as plain text and the API answers 400.referenceandbody: test values for now. Workflow 2 receivesbody.
Keep the key out of the node
Instead of Send Headers you can set Authentication to a Header Auth credential with the same name and value. The key is then stored once, in n8n’s credentials.
- In workflow 1, run the HTTP Request node (Execute step, or Execute workflow).
- The node’s output shows an
idandstatus: PENDING. - Wait about two minutes, then open workflow 2’s Executions. A new execution appears; its Webhook node shows
body.invoiceIdandbody.email.
When it worked: workflow 2 ran with your data. In its next nodes, map{{ $json.body.invoiceId }}and{{ $json.body.email }}.The node shows an error?
401: the Authorization header is missing or the key is wrong. 400: the JSON is not valid or
urlis not a public HTTPS address; the response says which field. Details in API errors.Open Dashboard → Jobs. The top row goes to your n8n host with the status Delivered. Click it: the attempt shows HTTP 200 and n8n’s answer.
When it worked: the job is Delivered.Failed with HTTP 404?
n8n answers 404 when workflow 2 is not active or the URL is the Test URL. Activate the workflow, copy the Production URL, then edit and reschedule.
- In the JSON, replace
$now.plus({ minutes: 2 })with$now.plus({ days: 3 }). - Replace each test value with a field from your trigger: delete
test-123(keep the quotes) and drag the invoice ID from the input panel in its place. Same for the email and the reference. - Activate workflow 1.
Map IDs, not free text
A value containing a double quote breaks the JSON.
referenceaccepts letters, digits and: . _ / -only. Free schedules up to 7 days ahead; Pro and Scale up to 365.When it worked: the next real run creates a job that Jobs shows as Scheduled, three days ahead.- In the JSON, replace
With the job id
Store the
idfrom step 4’s output with the invoice. In the workflow that handles the payment, add an HTTP Request node:HTTP Request · cancel by id
- Method
POST- URL
https://webhookscheduler.com/api/v1/jobs/JOB_ID/cancel, with the stored id in place ofJOB_ID- Send Headers
On- Header → Name
Authorization- Header → Value
Bearer wh_live_YOUR_KEY, with your key in place ofwh_live_YOUR_KEY. Keep the word Bearer and the space.- Send Body
Off
With your own reference
Add an HTTP Request node with Method
GET, URLhttps://webhookscheduler.com/api/v1/jobs, the same header, and two query parameters:reference=invoice:plus the invoice ID, andstate=open. Then a Split Out node on the fieldjobs, then the cancel node above with{{ $json.id }}in place ofJOB_ID.When it worked: the cancel answers 200 withstatus: CANCELED, and Jobs shows Canceled.A webhook can be canceled while it is Scheduled or Retrying. Canceled webhooks still count toward your monthly quota (100 a month on Free).
Next
If a delivery fails, the dashboard says why and you get an email: see Troubleshooting. The same flow in Make is in the Make quickstart.