Verifying the Circus Webhook JWT in n8n
Your workflow's webhook is a URL that starts a run. If you want to make sure only Circus can trigger it — not anyone who discovers the URL — you protect it with a JWT, and this article is how.
How Circus signs the request
When Circus dispatches a run, it can sign the webhook request with a short-lived JSON Web Token: HS256 algorithm, a 60-second expiry, signed with a per-engine shared secret. The token travels in the Authorization: Bearer header, and its payload identifies the specific execution. This is the only supported webhook authentication method today.
Configuring n8n to verify it
To turn verification on, you set up matching JWT auth in n8n:
- In n8n, create a JWT Auth credential with Algorithm: HS256 and Key type: Passphrase, and enter a shared secret of your choice.
- On your workflow's Webhook node, set Authentication to JWT Auth and select that credential.
- Give Circus the same secret so it signs with it. That's an operator/ops step — the secret goes in Circus's environment as the per-engine webhook shared secret (see the Ops docs; the demo's setup guide walks through it).
With that in place, n8n rejects any unsigned or wrongly-signed call before your workflow even runs. If you omit the secret on the Circus side, webhooks are simply sent unauthenticated — handy while you're wiring things up, but you'll want the secret set for anything real.
What the Init node checks
When JWT auth is on, n8n hands the verified token payload to your workflow, and the Init node validates it more strictly: that the token's subject matches the run's workflow_execution_id, that it was issued by Circus, and that its issued-at and expiry times are sane and unexpired.
One deliberate subtlety worth knowing: if that validation fails, Init does not call /terminate. A forged or mismatched token can't be trusted to identify a real execution on the platform, so terminating on it could act on the wrong run. Init simply fails the node, and the platform's background reconciler later cleans up the abandoned execution. It's a small design choice that keeps a bad token from ever touching a legitimate run.