npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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 5xx on a failed run so Hookdeck retries the run itself; Async mode exposes Event > Retry as a workflow step for error branches, plus isLastAttempt for 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.

  1. Open Hookdeck and select an Event Gateway project.
  2. Go to Project Settings → Secrets and copy the API key.
  3. 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 listen was running and dropped. The session stays eligible for two minutes, so the event is created and the attempt fails with CLI_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 200 at the edge whether or not a payload verified. The answer is the verified field 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 verified field 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:

  1. Set an Error Workflow on the workflow (Settings → Error Workflow), or add an error output branch.
  2. In it, add the Hookdeck Event Gateway node with Event → Retry.
  3. 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.idempotencyKey themselves.
  • Async Retry does not re-enqueue failed runs by itself. For the same reason — no completion hook — the trigger cannot call POST /events/{id}/retry when 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 503 with Retry-After when 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.

  1. Source → By Name, type a name.
  2. Publish the workflow. The node creates the source.
  3. Source → From List. Each source is listed as name — https://hkdk.events/....
  4. 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 listen is 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 dead-letter example running: a Stripe payment_intent.succeeded event
arriving through Hookdeck, taking the false branch of "Final attempt?" to
Process order, with the trigger's output showing body, headers, query and the
hookdeck metadata

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 spec

SourceTypes.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-hookdeck

Then 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.

  1. Land everything through PRs, including promoting ## [Unreleased] in CHANGELOG.md to the new version with a date.

  2. Check main is green.

  3. Draft the release notes. Write them for someone running a workflow: what changes for them, and whether they have to do anything.

  4. Create the release against main, tagged vMAJOR.MINOR.PATCH:

    gh release create v0.2.0 --target main --title v0.2.0 --notes-file notes.md

    Or 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.

Resources

License

MIT