@hookdeck/n8n-nodes-hookdeck
v0.1.0
Published
Use the Hookdeck Event Gateway from n8n: manage sources, destinations and connections, and trigger workflows from events Hookdeck delivers
Readme
@hookdeck/n8n-nodes-hookdeck
n8n community nodes for Hookdeck, the event gateway that receives, verifies, queues, and delivers webhooks, so your workflows don't have to care whether n8n was up when the event arrived.
n8n is a fair-code licensed workflow automation platform. Its built-in Webhook trigger hands your provider a URL that leads straight to your instance, which means a restart, a deploy, or a deactivated workflow loses events, a double-firing provider runs your workflow twice, and a failed execution has no event left to retry. This package puts Hookdeck in between, and makes its guarantees configurable from the node itself:
- No events lost to downtime. Events are queued durably at Hookdeck. Unpublishing a workflow pauses delivery instead of dropping events; publishing again delivers everything that arrived in the gap. Failed deliveries are retried, up to 50 attempts with exponential or linear backoff.
- Duplicates collapsed at ingest. A configurable deduplication window (60 s by default) means a provider retry costs one execution, not two. Every delivery carries a stable idempotency key for stricter checks in-workflow.
- Failed runs are recoverable. Sync acknowledgement returns
5xxon a failed run so Hookdeck retries the run itself; Async mode exposes Event > Retry as a workflow step for error branches, plusisLastAttemptfor dead-letter routing. - Signature verification at the edge. 151 source types (Stripe, Shopify, GitHub, Twilio, and more), each with that platform's own scheme, plus HMAC / API key / basic auth for generic sources. Verification runs once you supply the platform's signing secret — a source without one accepts unsigned payloads. Deliveries into n8n are separately signed and verified against the raw body.
- Rate limiting upstream of n8n. Cap delivery throughput or concurrency before events reach your instance, including per-customer limits keyed on a payload path, so a burst (or one busy tenant) can't take the instance down.
- Full visibility. Inspect events, delivery attempts, and the original requests from inside n8n, and retry or replay them as workflow steps.
This package adds two nodes:
- Hookdeck Event Gateway Trigger — starts a workflow when Hookdeck delivers an event. On activation it provisions the Hookdeck connection for you, and lists your sources with the public URL to give your provider. Retries, deduplication, and rate limits are node options, with no dashboard round-trips.
- Hookdeck Event Gateway — manages sources, destinations and connections, and inspects events, delivery attempts, requests and issues.
Scope. These nodes cover the Event Gateway only — the inbound path, where Hookdeck receives events on your behalf and delivers them to n8n. Hookdeck's other products are not covered here: notably Outpost, which is the outbound path for publishing events to your users' destinations. Outbound publishing would be a separate node, not an operation on these.
Installation
Follow the community nodes installation guide
and use the package name @hookdeck/n8n-nodes-hookdeck.
You'll also need a Hookdeck account; the free tier is enough to run real workflows on.
Documentation
Credentials
The nodes authenticate with the API key of a Hookdeck Event Gateway project.
- Open Hookdeck and select an Event Gateway project.
- Go to Project Settings → Secrets and copy the API key.
- In n8n, create a new Hookdeck Event Gateway API credential and paste the key.
One credential is one project. The key carries the project, so there is nothing else to configure and no way to reach across projects — everything these nodes create or read belongs to the project the key came from. To work with a second project, add a second credential.
A key from an Outpost project will not work here. Outpost is the outbound path, with its own API; these nodes only speak to the Event Gateway.
Hookdeck Event Gateway Trigger
Set a source name, pick the platform sending the events, and activate the workflow. On activation the node creates a Hookdeck connection whose destination is this workflow's webhook URL. The public source URL to give your provider is then listed under Source → From List — see Finding the source URL.
Events arrive through Hookdeck rather than directly, so Hookdeck's connection rules apply to them — retries, delivery rate limits and deduplication are configured on the node under Options.
An existing source is adopted, not rewritten. If a source of that name is already in the project, the node binds the connection to it by ID and leaves its Source Type and Verification exactly as they are — a source can feed several connections, and rewriting it would change how their events are verified too. The node's own Source Type and Verification apply only when it creates the source. Note that this holds even when the types agree: a Webhook Secret or HMAC setting entered here does not reach a source that already exists. Whenever a setting is ignored, n8n's server log names it.
To deliberately reconfigure an existing source, turn on Options → Update Existing Source. That applies this node's settings to the source, and to every connection fed by it.
How events reach n8n
On wording. n8n 2.x calls making a workflow live publishing — the button reads Publish, and Unpublish to take it down. Older versions, the REST API, and the node option On Deactivate still say activate and deactivate. They are the same thing.
The trigger picks one of two delivery routes when you publish the workflow, based on whether Hookdeck can reach the address n8n advertises.
| n8n's webhook URL | Destination | How events arrive |
| --- | --- | --- |
| Publicly reachable — n8n Cloud, or self-hosted with a public address | HTTP | Hookdeck makes a request to n8n directly |
| Not reachable — a laptop, or an instance behind NAT | CLI | Hookdeck sends events to hookdeck listen, which forwards them to n8n |
There is nothing to configure. Both routes sign deliveries the same way, so signature verification behaves identically and the workflow receives the same item either way.
This is about reachability, not about development. A self-hosted n8n behind NAT uses the CLI route in production just as a laptop does.
Running the CLI route
The node writes the exact commands to n8n's server log when the workflow is published — that is the console n8n itself is running in, not the Logs panel in the editor, which only shows workflow executions. Run them alongside n8n:
hookdeck ci --api-key <your Event Gateway project API key>
hookdeck listen 5678 <source> --device-name n8n-<host>-<instance>hookdeck ci matters: hookdeck listen otherwise uses whichever project the
CLI was last logged into, and pointing it at the wrong one looks like the node
is broken. --device-name keeps two n8n instances from being treated as one
listener restarting.
No connection is named in that command, deliberately. An n8n trigger has two webhook URLs — the live one used while the workflow is active, and a separate one used by Listen for test event in the editor. The node provisions a Hookdeck connection for each, both on the same source. Naming a connection attaches the CLI to that one alone, so a command naming the live connection would leave test events with no CLI session — and events for a connection with no session are not recorded at all. Naming only the source attaches to every connection the source has.
The CLI picks up the connections that exist when it starts, so restart it after the first use of Listen for test event, which is when n8n creates the second connection.
What the CLI route cannot do
- No Delivery Rate Limit and no Delivery Group. Hookdeck supports these on directly reachable destinations only. If they are set, the node does not send them and says so in the log.
- Events are not held for a listener that is not there. Two cases, and the
second is the one that bites:
hookdeck listenwas running and dropped. The session stays eligible for two minutes, so the event is created and the attempt fails withCLI_UNAVAILABLE. The connection's retry rule then applies — five exponential retries from a minute apart by default, roughly half an hour of recovery. Beyond that, retry it by hand.- No CLI session exists at all. No event is created for that connection. Measured: with two CLI connections on one source and a listener on only one of them, the listened connection recorded two events and the unlistened one recorded zero. There is nothing queued, nothing failed, and nothing to retry — the delivery simply is not recorded against that connection.
That last point decides whether this route suits production, and the answer depends on how you run the CLI. A terminal window on a laptop is not production whatever the retry settings. A supervised process on a server — a systemd unit, or a container with a restart policy — keeps outages to seconds, which the retry rule covers.
Parameters
| Parameter | Description | | --- | --- | | Source | The Hookdeck source. From List shows every source in the project with its public URL, so you can copy the URL for your provider without leaving the canvas. By Name takes a new name — letters, numbers, hyphens and underscores — and creates the source on publish. | | Source Type | The platform sending events. This selects which signature scheme Hookdeck applies; it does not switch verification on by itself — see below. Use Webhook (Generic) to configure verification yourself. Applies when the node creates the source; an existing source keeps its own type. | | Verification | For generic sources: HMAC, API Key, Basic Auth, or none. | | Webhook Secret | For platform sources: the signing secret the platform issued you. Placed in whichever field that platform expects. A few platforms need more than one value — those ask you to use Source Config (JSON) instead, and name the fields. |
Verification only starts when a secret is set
Choosing a Source Type tells Hookdeck which signature scheme that platform
uses. It does not enable verification on its own. A source typed STRIPE with
no signing secret accepts an unsigned, forged payload and delivers it — the edge
answers 200 and records verified: false.
So fill in Webhook Secret for platform sources, or set Verification for generic ones. Two things make this easy to miss:
- The status code is not the verdict. Hookdeck answers
200at the edge whether or not a payload verified. The answer is theverifiedfield on the request, under Request → Get (or the dashboard). Read the request detail, not the list — the list omits fields the detail returns. - A configured source looks identical to an unconfigured one. The API never
returns the secret, or any indication one exists, so you cannot confirm it
from the source itself. An inbound request's
verifiedfield is the only signal.
This is separate from Verify Signature under Options, which covers the Hookdeck-to-n8n hop and is on by default.
Options
| Option | Description |
| --- | --- |
| Retry Strategy / Count / Interval | How Hookdeck retries a delivery this workflow did not accept. Up to 50 attempts. Server errors and 429 are always retryable. Defaults to 5 exponential retries a minute apart, applied even if you never open Options. |
| Deduplication Window | Discard repeat events seen within the window, so a double-firing provider costs one execution. Defaults to 60000 ms; set 0 to turn it off. |
| Delivery Rate Limit / Period | Cap how fast Hookdeck delivers into this workflow. Supports concurrent to cap simultaneous executions. |
| Delivery Group Key / Rate Limit / Period | Group deliveries by a payload path, so each customer or repository gets its own rate limit and one busy sender cannot crowd out the rest. |
| On Deactivate | Pause the connection (default) or delete it. See below. |
| Header Prefix | Prefix of Hookdeck's metadata headers. Change only for a white-labelled project. |
| Update Existing Source | Apply this node's Source Type and Verification to a source that already exists. Off by default — see above. |
| Verify Signature | Reject deliveries that are not signed by Hookdeck. On by default. |
| Source Config (JSON) | Advanced. Merged into the source config, for verification schemes the fields above cannot express. |
Acknowledgement modes
| Mode | Behaviour | | --- | --- | | Async Retry (default) | Acknowledge as soon as the event is received, then run the workflow. The sender never waits. A run that fails afterwards is not retried by Hookdeck, because the delivery already succeeded. | | Sync | Hold the HTTP response until the workflow finishes. Success answers 2xx; a failed run answers 5xx, so Hookdeck's retry rules apply to the run, not just the delivery. |
Hookdeck stops waiting after 60 seconds, so Sync suits workflows that finish well inside that. Longer workflows should use Async Retry.
Retrying a failed run under Async Retry
In Async Retry mode the delivery has already succeeded by the time the workflow runs, so Hookdeck will not retry it on your behalf. Hookdeck does allow a successful event to be retried manually, which makes the recovery path a step you add to the workflow rather than something the trigger can do for you:
- Set an Error Workflow on the workflow (Settings → Error Workflow), or add an error output branch.
- In it, add the Hookdeck Event Gateway node with Event → Retry.
- Set the Event ID to the failing execution's event:
{{ $json.hookdeck.eventId }}.
Hookdeck then redelivers the event and the workflow runs again, with
hookdeck.attemptTrigger set to MANUAL. Guard against loops by checking
hookdeck.attemptCount before retrying.
Prefer Sync where the workflow is fast enough: it gets the same behaviour from Hookdeck's own retry rules with nothing extra to build.
Unpublishing without losing events
Deleting a Hookdeck connection cancels every event still queued for it, and that cannot be undone. So unpublishing the workflow pauses the connection by default: inbound events are held durably, and publishing again unpauses it and delivers everything that arrived meanwhile. That makes a deploy or a maintenance window lossless.
Choose Delete the Connection under Options if you would rather the connection be removed — accepting that queued events go with it.
Output
Each execution receives one item:
{
"body": { "...": "the payload the provider sent" },
"headers": { "...": "all request headers" },
"query": { "...": "query string parameters" },
"hookdeck": {
"eventId": "evt_...",
"requestId": "req_...",
"attemptCount": 1,
"attemptTrigger": "INITIAL",
"willRetryAfter": "60",
"isLastAttempt": false,
"sourceName": "stripe-production",
"idempotencyKey": "evt_..."
}
}hookdeck.isLastAttempt is true when Hookdeck will not retry the event again
automatically — the natural condition for a dead-letter branch.
hookdeck.idempotencyKey is stable across retries of the same event, so it is a
sound deduplication key. Note that a replay creates a new event with a new ID;
use hookdeck.requestId if you need to recognise replayed traffic.
Limitations
This node follows a reliability contract shared with the Hookdeck plugins for other hosts. Two parts of it work differently here, because n8n requires it:
- No local deduplication ledger. The shared contract deduplicates on
"admit when the attempt number exceeds the highest recorded for this event
ID". A trigger node is invoked before the workflow runs and has no completion
hook, so it cannot record whether a run succeeded. Deduplication is therefore
delegated to Hookdeck's own Deduplication Window rule, which collapses
repeat events at ingest. Workflows needing stricter guarantees should key on
hookdeck.idempotencyKeythemselves. - Async Retry does not re-enqueue failed runs by itself. For the same
reason — no completion hook — the trigger cannot call
POST /events/{id}/retrywhen a run fails. The capability is exposed instead, as a workflow step: see Retrying a failed run under Async Retry above. Sync mode needs none of this, because a failed run answers 5xx and Hookdeck's retry rules apply directly. - No host-side admission control. The contract answers
503withRetry-Afterwhen a concurrency cap is reached. n8n governs its own execution concurrency, so the equivalent lever here is the Delivery Rate Limit option, which caps delivery inside Hookdeck before n8n is reached — on a directly reachable n8n. Instances receiving events through the Hookdeck CLI have no equivalent, because a CLI destination does not support rate limiting.
Destination authentication also uses CUSTOM_SIGNATURE rather than
HOOKDECK_SIGNATURE. Hookdeck's project signing secret is not exposed through
the API, so HOOKDECK_SIGNATURE would force you to copy a second secret by
hand. This node generates its own signing secret at provisioning time instead;
the algorithm is identical (HMAC-SHA256 over the raw body, base64).
Finding the source URL
This is the address you give your provider. It is https://hkdk.events/<source id>,
and Hookdeck generates that ID when the source is created, so it cannot be
predicted from the name and does not exist until the source does.
- Source → By Name, type a name.
- Publish the workflow. The node creates the source.
- Source → From List. Each source is listed as
name — https://hkdk.events/.... - Give that URL to Stripe, GitHub, or whatever is sending the events.
There is no need to know the URL before publishing. Nothing can arrive until your provider has been pointed at it, so publishing first costs nothing.
If the source already exists in Hookdeck, skip to step 3 — pick it from the list, and the node leaves its Source Type and Verification alone. The link beside the field in By Name mode opens Hookdeck's create-a-source page, if you would rather make it there first.
To get the URL onto your clipboard, either use the link beside a listed source,
which opens it in the Hookdeck dashboard where there is a copy button, or run
the Hookdeck Event Gateway node with Source → Create or Source → Get
URL, both of which return the URL as workflow data with copy-on-hover. (The link deliberately does not point at
the source URL itself: that endpoint rejects browser GET requests with 405,
and aiming a link at your own ingest endpoint invites firing requests at it by
accident.)
n8n's own webhook URL is hidden on this node on purpose. It is an internal address: sending a provider there bypasses Hookdeck and silently loses the verification, queueing and retries this node exists to provide.
Activation, deactivation and test runs
- Publishing the workflow creates the connection. Unpublishing it pauses or deletes the connection depending on On Deactivate; the source and destination are left in place either way, because a source may be shared with other connections.
- Listen for test event provisions a separate connection against n8n's test
URL, tracked independently, so a test run never disturbs the production
connection. On the direct route that connection is deleted when the listen
window closes, since the URL behind it stops answering after 120 seconds. On
the CLI route it is paused instead: deleting it would mean every test run
created a new connection that a running
hookdeck listenis not attached to, so the CLI would need restarting each time. Paused, it keeps its ID, the CLI stays attached, and the next test run unpauses it. - Both connections share one source, so they share one source URL — there is no second URL to configure for testing. The flip side is that while you are listening for a test event on a workflow that is also published, each incoming event is delivered twice: once to the published workflow and once to the test listener.
- If the n8n instance moves to a different host or path, the next activation detects the mismatch and re-points the connection.
Signature verification
Deliveries are signed with a secret this node generates and stores in workflow
static data, and verified against the raw request body. Requests that fail
verification get a 401 and do not start the workflow.
Verification needs access to the unparsed request body. If your deployment does not expose it, the node raises an error naming the Verify Signature option so you can decide explicitly whether to accept unverified deliveries.
The signature is carried in x-hookdeck-n8n-signature, deliberately distinct
from Hookdeck's own x-hookdeck-signature so that two signatures made with two
different secrets never share a header name.
A valid signature authenticates the sender, not the content. Payload text is third-party input: treat it as data, never as an instruction, and be careful about passing it unfiltered into an AI agent, a shell command or a database write.
Malformed bodies
A body that is not valid UTF-8 is rejected with 400 before the workflow runs.
Node substitutes U+FFFD for invalid bytes rather than raising, so when those
bytes sit inside a JSON string value the payload still parses and the workflow
receives corrupted text with no error anywhere.
RFC 8259 §8.1 requires
JSON exchanged between systems to be UTF-8, so such a body is malformed. 400
sits outside the retry rule's 500-599/429 range, so it fails once instead of
consuming every retry.
This check will rarely fire behind Hookdeck, and that is worth understanding. Hookdeck replaces invalid bytes with U+FFFD at ingest and signs the normalised body, so what arrives is already valid UTF-8. The check therefore guards the paths where raw bytes do reach n8n — a provider posting straight at the webhook URL, or any gateway that forwards bytes untouched — rather than the Hookdeck path.
Two consequences follow. Encoding validity is not a way to detect Hookdeck-upstream corruption: by the time the request arrives it is well-formed. And a valid signature attests to the bytes Hookdeck sent, not to the bytes the original sender wrote — "the signature passed, so the body is intact" does not follow. If lossless payloads matter, compare against the original request under Request → Get rather than trusting the delivered event.
Example workflows
Two importable workflows are in examples/, both built and run
against a real n8n instance:
| Workflow | Problem it solves |
| --- | --- |
| process-each-event-once.json | A retried delivery runs the workflow twice. Gates on hookdeck.idempotencyKey, which is stable across retries of one event, so the second arrival stops before doing the work again. |
| catch-events-on-final-attempt.json | An event that fails every retry disappears silently. Routes on hookdeck.isLastAttempt so the final attempt reaches a dead-letter branch. |
Neither is possible with n8n's built-in Webhook node, because both depend on
delivery metadata only a gateway can supply. See
examples/README.md for how each behaves and what was
observed running them.

The hookdeck column is the part n8n's own Webhook node cannot give you:
isLastAttempt is what the IF branches on, idempotencyKey is what the other
example deduplicates on, and eventUrl links straight to that delivery in the
Hookdeck dashboard.
Hookdeck Event Gateway node
| Resource | Operations | | --- | --- | | Attempt | Get, Get Many | | Connection | Get, Get Many, Get Count, Delete, Pause, Unpause | | Destination | Get, Get Many, Get Count | | Event | Get, Get Many, Get Count, Retry, Mute, Cancel | | Issue | Get, Get Many, Get Count, Update, Dismiss | | Request | Get, Get Many, Retry | | Source | Get or Create, Get, Get Many, Get Count, Get URL |
Source → Get or Create returns the named source, creating it only if it is
not there, and gives back its public URL. Source names are unique within a
project, so a plain create is not safe to re-run — POST /sources answers 409
the second time. An upsert would be, but PUT /sources rewrites an existing
source's type and verification, which is the damage the trigger was changed to
stop doing. Getting first avoids both.
Get Many supports Return All, which walks Hookdeck's pagination, or a Limit.
Get Count answers "how many" without listing them, and takes the same
filters. Connections, destinations, issues and sources are counted exactly and
return isAtLeast: false. Events are different — Hookdeck exposes no event
count — so they are counted by paging to a ceiling and returning
isAtLeast: true when that ceiling is reached, alongside countedUpTo. Treat
that as a floor, not a total. This matters most when the node is used as an AI
agent tool: a page size reported as a count is a number the agent will state as
fact.
Compatibility
Built against Hookdeck API version 2025-07-01, and verified end to end on n8n
2.34.4 with Node.js 22.23.2: package loaded from N8N_CUSTOM_EXTENSIONS,
credential created and its test passing, workflow activated, a live event sent
through the source URL and received by the workflow with its signature verified,
forged requests rejected with 401, and an event sent while deactivated held and
then delivered on reactivation.
It targets n8nNodesApiVersion: 1, which n8n 1.x also supports, but only 2.x has
been tested — if you run 1.x, treat it as unverified rather than assumed working.
Development
Layout
credentials/
HookdeckEventGatewayApi.credentials.ts API key credential and its test request
nodes/Hookdeck/
HookdeckEventGatewayTrigger.node.ts trigger: provisioning lifecycle + delivery handling
HookdeckEventGateway.node.ts action node: resource/operation dispatch
descriptions/
TriggerProperties.ts trigger UI
ActionProperties.ts action node UI
ConnectionPayload.ts what we ask Hookdeck to provision
Registration.ts what the trigger persists between activations
Delivery.ts verifying and describing an inbound delivery
Naming.ts Hookdeck naming rules, reachability checks
GenericFunctions.ts HTTP transport, error mapping, pagination
SourceTypes.ts generated source-type list (do not hand-edit)The UI definitions live apart from the nodes because they are long and rarely
the thing you are reading the code for. Delivery.ts and Naming.ts are free
of n8n imports on purpose — they encode Hookdeck's rules rather than n8n's, and
are shared with the Hookdeck plugins for other hosts.
Commands
npm install
npm run build
npm test # builds, then runs the unit suite with node:test
npm run lint # n8n's community-node rules
npm run scan # the same checks n8n runs when reviewing for verification
npm run verify:load # loads the built package the way n8n loads it
HOOKDECK_EG_API_KEY=... npm run test:integration # live tests against the API
npm run generate:source-types # rewrite SourceTypes.ts from Hookdeck's OpenAPI spec
npm run check:source-types # fail if it has drifted from the specSourceTypes.ts is generated, so the ~150 platform types and their auth shapes
are never hand-maintained. A scheduled workflow runs check:source-types weekly
rather than blocking every pull request, because the spec is a live third-party
document and a Hookdeck release would otherwise fail unrelated CI.
The integration suite is skipped unless HOOKDECK_EG_API_KEY is set, so the
default npm test needs no credentials. It creates and deletes real sources,
destinations and connections — point it at a throwaway Event Gateway project,
never one carrying live traffic. In CI it runs from a repository secret of the
same name, and is skipped for pull requests from forks, which cannot read it.
npm run scan is the one that matters before submitting: it runs
@n8n/scan-community-package against this working tree, with inline
eslint-disable comments ignored exactly as the real review does.
To try the nodes in a real n8n:
npm link
mkdir -p ~/.n8n/custom && cd ~/.n8n/custom && npm init -y && npm link @hookdeck/n8n-nodes-hookdeckThen start n8n with ./scripts/run-n8n.sh. n8n needs Node 22.22 or newer — the
script checks and refuses otherwise. If your default is older, install one
(asdf install nodejs 22.23.2) and point NODE_BIN at it rather than changing
the machine default.
No tunnel is needed. Publish the workflow; the node sees that Hookdeck cannot reach this n8n, provisions a CLI destination, and writes the commands to run to its server log:
hookdeck ci --api-key <your Event Gateway project API key>
hookdeck listen 5678 <source> <connection> --device-name n8n-<host>-<instance>A tunnel still works if you prefer one — set WEBHOOK_URL to the public address
before starting n8n and the node provisions an HTTP destination instead. See
How events reach n8n for what differs between the two.
Releasing
Publishing is driven by a GitHub Release, not by a tag push and never from a laptop. n8n requires community nodes to be published from GitHub Actions with an npm provenance statement, so the publish has to happen in CI.
Land everything through PRs, including promoting
## [Unreleased]in CHANGELOG.md to the new version with a date.Check
mainis green.Draft the release notes. Write them for someone running a workflow: what changes for them, and whether they have to do anything.
Create the release against
main, taggedvMAJOR.MINOR.PATCH:gh release create v0.2.0 --target main --title v0.2.0 --notes-file notes.mdOr use the GitHub UI — Releases → Draft a new release.
Publishing then happens automatically:
publish.yml checks out the tag, takes the
version from it, re-runs lint, the verification scan, the build, the load check
and the unit tests, and publishes with provenance. A release marked
pre-release publishes under the beta dist-tag instead of latest.
There is no release commit — the tag is the version, and package.json in git
is not bumped to match.
Bootstrapping the package (once, already done after the first release)
npm configures trusted publishers on a package that already exists, so a package that has never been published cannot use OIDC for its first publish. The name is claimed by hand once, and everything anyone installs is published from CI with provenance.
npm login
RELEASE_MODE=true npm publish --access public # claims the name at 0.0.1
npm deprecate @hookdeck/[email protected] \
"Placeholder to claim the package name. Use 0.1.0 or later."RELEASE_MODE is needed because prepublishOnly runs a guard that blocks
publishing by hand — this is the one sanctioned exception to it.
Then on npmjs.com: the package → Settings → Trusted Publishers → Add a publisher
→ GitHub Actions, owner hookdeck, repository n8n-nodes-hookdeck, workflow
publish.yml, environment blank, allowed action npm publish. Environment must
be blank because publish.yml declares no environment:, and npm matches the
OIDC claim exactly.
No NPM_TOKEN secret is needed and none should be added. npm finds the trusted
publisher itself and exchanges the Actions OIDC token during publish; a token in
.npmrc takes precedence over OIDC, so a stale or empty secret quietly becomes
the publishing identity, or fails the publish.
That throwaway 0.0.1 is the only version ever published without provenance,
and it is deprecated the moment it exists. 0.1.0 onwards go through
publish.yml.
Choosing the version
SemVer here is about the contract with a saved workflow. n8n records the node type, the credential type and every parameter name inside the user's workflow JSON, so renaming any of them detaches existing workflows without failing a build. That is a MAJOR change, whatever it looks like in the diff.
| Change | Bump | | --- | --- | | Renamed or removed node type, credential type, parameter, resource or operation; changed output item shape; a default that alters delivery behaviour | MAJOR | | New resource, operation or option; additive output fields; new source types | MINOR | | Fixes, wording, icons, tests, CI, dependency bumps | PATCH |
Agents: skills/n8n-nodes-hookdeck-release
carries the full checklist, the gates and a notes template.
