{"_id":"@comers/n8n-nodes-comers","_rev":"5-c889f9ae3eb9882fbdef87b4c60ad170","name":"@comers/n8n-nodes-comers","dist-tags":{"latest":"0.3.1"},"versions":{"0.1.0":{"name":"@comers/n8n-nodes-comers","version":"0.1.0","keywords":["n8n-community-node-package","comers","webhook","trigger","events"],"author":{"name":"Damian Nosowicz","email":"damian@nzt.li"},"license":"MIT","_id":"@comers/n8n-nodes-comers@0.1.0","maintainers":[{"name":"rocketdeploy.dev","email":"damian@nzt.li"}],"homepage":"https://github.com/rocketdeploy-dev/n8n-nodes-comers","bugs":{"url":"https://github.com/rocketdeploy-dev/n8n-nodes-comers/issues"},"n8n":{"nodes":["dist/nodes/ComersTrigger/ComersTrigger.node.js"],"strict":true,"credentials":["dist/credentials/ComersWebhookSecretApi.credentials.js"],"n8nNodesApiVersion":1},"dist":{"shasum":"96654c511d19f60068f723a2e45f7ca9c75f042f","tarball":"https://registry.npmjs.org/@comers/n8n-nodes-comers/-/n8n-nodes-comers-0.1.0.tgz","fileCount":21,"integrity":"sha512-sBMIT9HYemsi+JmDywFfFxT0NcHgbjFnsQlQNJTPqVh+NoaHIjs1SX2OpvvtinDYpEjaE3lWmpIhSzu7uaLcUA==","signatures":[{"sig":"MEUCIBU9opiA1ivTA+5Ht6fiHBIky7uLdC5oqKfqiscjn4iLAiEA9PcMkfzE6utvS6BvKWJTBFSTxhFeR2BN9KsKESUHS3g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@comers%2fn8n-nodes-comers@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":58068},"gitHead":"6c4ecca801d1052810201a89e7b247b27012a32a","scripts":{"dev":"n8n-node dev","lint":"n8n-node lint","scan":"node scripts/scan-package.mjs","test":"vitest run","build":"n8n-node build","release":"n8n-node release","lint:fix":"n8n-node lint --fix","check:tag":"node scripts/release-tag.mjs","pack:check":"node scripts/check-tarball.mjs","test:watch":"vitest","build:watch":"tsc --watch","prepublishOnly":"node scripts/prepublish.mjs"},"_npmUser":{"name":"rocketdeploy.dev","email":"damian@nzt.li"},"release-it":{"git":{"tagName":"${version}","commitMessage":"chore: release ${version}","tagAnnotation":"Release ${version}"},"npm":{"publish":false}},"repository":{"url":"git+https://github.com/rocketdeploy-dev/n8n-nodes-comers.git","type":"git"},"_npmVersion":"12.0.2","description":"Receive signed Comers domain events in n8n","directories":{},"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.9.0","eslint":"9.32.0","vitest":"^4.1.9","prettier":"3.6.2","release-it":"^19.0.4","typescript":"5.9.2","@n8n/node-cli":"^0.47.2","@n8n/scan-community-package":"^0.35.0"},"peerDependencies":{"n8n-workflow":"*"},"_npmOperationalInternal":{"tmp":"tmp/n8n-nodes-comers_0.1.0_1789128110509_0.5474692636148177","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@comers/n8n-nodes-comers","version":"0.1.1","keywords":["n8n-community-node-package","comers","webhook","trigger","events"],"author":{"name":"Damian Nosowicz","email":"damian@nzt.li"},"license":"MIT","_id":"@comers/n8n-nodes-comers@0.1.1","maintainers":[{"name":"rocketdeploy.dev","email":"damian@nzt.li"}],"homepage":"https://github.com/rocketdeploy-dev/n8n-nodes-comers","bugs":{"url":"https://github.com/rocketdeploy-dev/n8n-nodes-comers/issues"},"n8n":{"nodes":["dist/nodes/ComersTrigger/ComersTrigger.node.js"],"strict":true,"credentials":["dist/credentials/ComersWebhookSecretApi.credentials.js"],"n8nNodesApiVersion":1},"dist":{"shasum":"4556cbfe3ffcafd5998b5563d351fefa167663d0","tarball":"https://registry.npmjs.org/@comers/n8n-nodes-comers/-/n8n-nodes-comers-0.1.1.tgz","fileCount":21,"integrity":"sha512-c6uZ/YScrNXcuD5FyhE7RqbG3Yq9f2nLGLaHL4AqOzCNU7B/efSdefhplYquUPlHAvkYFwNMMtr5byjwUZqUOA==","signatures":[{"sig":"MEUCIExCGc7uMvmCT1Nvn8D9BYuP+nOlZgLWIClpCEOuqcG+AiEAp27SdlMoQoBEmhDBZkHYvxjs3h1Lr75OafBxIsOlDgk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCeVgdxqP/E5w8HJg4rvYWwR+TXhKji8ANoVnDZKr02AgIgcwLU/HbSOyP42aSJP/bkxA7k3grrM0RRkZn5ioFpyTg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@comers%2fn8n-nodes-comers@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":62279},"gitHead":"87b2a27b4e1c28ec873f11b3a014aaf949acbfb3","scripts":{"dev":"n8n-node dev","lint":"n8n-node lint","scan":"node scripts/scan-package.mjs","test":"vitest run","build":"n8n-node build","release":"node scripts/release.mjs","lint:fix":"n8n-node lint --fix","check:tag":"node scripts/release-tag.mjs","pack:check":"node scripts/check-tarball.mjs","test:watch":"vitest","build:watch":"tsc --watch","prepublishOnly":"node scripts/prepublish.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cf7e7509-a05b-41c6-931f-46b8b7bddbc7"}},"release-it":{"git":{"tag":true,"push":true,"commit":true,"tagName":"${version}","commitMessage":"chore: release ${version}","requireBranch":"main","tagAnnotation":"Release ${version}","requireCommits":true,"requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":false},"hooks":{"after:bump":"node scripts/finalize-changelog.mjs","before:bump":"node scripts/finalize-changelog.mjs --check-version ${version}","before:init":["node scripts/finalize-changelog.mjs --check","npm run lint","npm run build","npm test","npm run scan","npm run pack:check"]},"github":{"release":false}},"repository":{"url":"git+https://github.com/rocketdeploy-dev/n8n-nodes-comers.git","type":"git"},"_npmVersion":"12.0.2","description":"Receive signed Comers domain events in n8n","directories":{},"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.9.0","eslint":"9.32.0","vitest":"^4.1.9","prettier":"3.6.2","release-it":"^19.0.4","typescript":"5.9.2","@n8n/node-cli":"^0.47.2","@n8n/scan-community-package":"^0.35.0"},"peerDependencies":{"n8n-workflow":"*"},"_npmOperationalInternal":{"tmp":"tmp/n8n-nodes-comers_0.1.1_1789206836925_0.650584680643759","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@comers/n8n-nodes-comers","version":"0.2.0","keywords":["n8n-community-node-package","comers","webhook","trigger","events"],"author":{"name":"Damian Nosowicz","email":"damian@nzt.li"},"license":"MIT","_id":"@comers/n8n-nodes-comers@0.2.0","maintainers":[{"name":"rocketdeploy.dev","email":"damian@nzt.li"}],"homepage":"https://github.com/rocketdeploy-dev/n8n-nodes-comers","bugs":{"url":"https://github.com/rocketdeploy-dev/n8n-nodes-comers/issues"},"n8n":{"nodes":["dist/nodes/ComersTrigger/ComersTrigger.node.js"],"strict":true,"credentials":["dist/credentials/ComersApi.credentials.js"],"n8nNodesApiVersion":1},"dist":{"shasum":"8e0b147d79bdf94d1acb5054ae599294f6ae2dce","tarball":"https://registry.npmjs.org/@comers/n8n-nodes-comers/-/n8n-nodes-comers-0.2.0.tgz","fileCount":27,"integrity":"sha512-c1eWzlus6xYtCVjxsSxS9SZtfo3b0J2v4VIYJardJSyaxHFVqNtnhQN4SNXu9zdkg9d+XABU8SDWQVjDfrJamA==","signatures":[{"sig":"MEUCIA56mPzQcNBrCzFxpoCUlOSI/24O7maVW3K6zKFwtTgvAiEA226GLBdxr7DemIo4zhbaqPL9FjUK5+j8C7wPo9M/9gU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIHpUZ7B7f5xfk0hzWHO6EfDb8V8vG6+aqWulySMJlgSnAiEA4RlVwW/V1OjIN5zbRuMwR/Qtxe8ysGdL3wKh1Ka4m2s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@comers%2fn8n-nodes-comers@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":110262},"gitHead":"31dd7e27d8993769430ef25e5d044e7ac8d79e90","scripts":{"dev":"n8n-node dev","lint":"n8n-node lint","scan":"node scripts/scan-package.mjs","test":"vitest run","build":"n8n-node build","release":"node scripts/release.mjs","lint:fix":"n8n-node lint --fix","check:tag":"node scripts/release-tag.mjs","pack:check":"node scripts/check-tarball.mjs","test:watch":"vitest","build:watch":"tsc --watch","prepublishOnly":"node scripts/prepublish.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cf7e7509-a05b-41c6-931f-46b8b7bddbc7"}},"release-it":{"git":{"tag":true,"push":true,"commit":true,"tagName":"${version}","commitMessage":"chore: release ${version}","requireBranch":"main","tagAnnotation":"Release ${version}","requireCommits":true,"requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":false},"hooks":{"after:bump":"node scripts/finalize-changelog.mjs","before:bump":"node scripts/finalize-changelog.mjs --check-version ${version}","before:init":["node scripts/finalize-changelog.mjs --check","npm run lint","npm run build","npm test","npm run scan","npm run pack:check"]},"github":{"release":false}},"repository":{"url":"git+https://github.com/rocketdeploy-dev/n8n-nodes-comers.git","type":"git"},"_npmVersion":"12.0.2","description":"Receive Comers domain events in n8n, with automatic subscription management","directories":{},"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.9.0","eslint":"9.32.0","vitest":"^4.1.9","prettier":"3.6.2","release-it":"^19.0.4","typescript":"5.9.2","@n8n/node-cli":"^0.47.2","@n8n/scan-community-package":"^0.35.0"},"peerDependencies":{"n8n-workflow":"*"},"_npmOperationalInternal":{"tmp":"tmp/n8n-nodes-comers_0.2.0_1790095418594_0.310779014044422","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@comers/n8n-nodes-comers","version":"0.3.0","keywords":["n8n-community-node-package","comers","webhook","trigger","events"],"author":{"name":"Damian Nosowicz","email":"damian@nzt.li"},"license":"MIT","_id":"@comers/n8n-nodes-comers@0.3.0","maintainers":[{"name":"rocketdeploy.dev","email":"damian@nzt.li"}],"homepage":"https://github.com/rocketdeploy-dev/n8n-nodes-comers","bugs":{"url":"https://github.com/rocketdeploy-dev/n8n-nodes-comers/issues"},"n8n":{"nodes":["dist/nodes/ComersTrigger/ComersTrigger.node.js"],"strict":true,"credentials":["dist/credentials/ComersApi.credentials.js"],"n8nNodesApiVersion":1},"dist":{"shasum":"b3fe6e86e814981f9a804cfc65091d3bc5ba4759","tarball":"https://registry.npmjs.org/@comers/n8n-nodes-comers/-/n8n-nodes-comers-0.3.0.tgz","fileCount":30,"integrity":"sha512-PMQgPKt1XTCPFwSRQ5S//a1WQlxaxwskwwpAhlE8qxk7aVyq9s0xPYhwhWpx0Ycq6SxUWbufy/hzJ0AcIqxK9g==","signatures":[{"sig":"MEUCIQCqT2pqSv1Cm4bSIgHQaT0mwswAPPnNZ1XBfP04qWlxyAIgE2yxYpsvg4vNgbmcQtl5aucmOaBTTFpKdoSdJyro3Zs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQDYTbtlfj4pB4rtHXw5qI1Vus0PPN1eoEmDpltDijaFvwIgT5qfPV3p7LhncTtaRdsed3VMD3YnB7UoMZiZGQmsouA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@comers%2fn8n-nodes-comers@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":121872},"gitHead":"53b2644b215a54fcfcc87d45b656393a50985024","scripts":{"dev":"n8n-node dev","lint":"n8n-node lint","scan":"node scripts/scan-package.mjs","test":"vitest run","build":"n8n-node build","release":"node scripts/release.mjs","lint:fix":"n8n-node lint --fix","check:tag":"node scripts/release-tag.mjs","pack:check":"node scripts/check-tarball.mjs","test:watch":"vitest","build:watch":"tsc --watch","prepublishOnly":"node scripts/prepublish.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:cf7e7509-a05b-41c6-931f-46b8b7bddbc7"}},"release-it":{"git":{"tag":true,"push":true,"commit":true,"tagName":"${version}","commitMessage":"chore: release ${version}","requireBranch":"main","tagAnnotation":"Release ${version}","requireCommits":true,"requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":false},"hooks":{"after:bump":"node scripts/finalize-changelog.mjs","before:bump":"node scripts/finalize-changelog.mjs --check-version ${version}","before:init":["node scripts/finalize-changelog.mjs --check","npm run lint","npm run build","npm test","npm run scan","npm run pack:check"]},"github":{"release":false}},"repository":{"url":"git+https://github.com/rocketdeploy-dev/n8n-nodes-comers.git","type":"git"},"_npmVersion":"12.0.2","description":"Receive Comers domain events in n8n, with automatic subscription management","directories":{},"_nodeVersion":"24.21.0","_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.9.0","eslint":"9.32.0","vitest":"^4.1.9","prettier":"3.6.2","release-it":"^19.0.4","typescript":"5.9.2","@n8n/node-cli":"^0.47.2","@n8n/scan-community-package":"^0.35.0"},"peerDependencies":{"n8n-workflow":"*"},"_npmOperationalInternal":{"tmp":"tmp/n8n-nodes-comers_0.3.0_1790199424170_0.855626313014271","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"_id":"@comers/n8n-nodes-comers@0.3.1","n8n":{"nodes":["dist/nodes/ComersTrigger/ComersTrigger.node.js"],"strict":true,"credentials":["dist/credentials/ComersApi.credentials.js"],"n8nNodesApiVersion":1},"bugs":{"url":"https://github.com/rocketdeploy-dev/n8n-nodes-comers/issues"},"dist":{"shasum":"9667952b11f2e6dc4ce0f759e71fccc177b15061","tarball":"https://registry.npmjs.org/@comers/n8n-nodes-comers/-/n8n-nodes-comers-0.3.1.tgz","fileCount":30,"integrity":"sha512-nUUnRqwG70wtQ4iDUeIPr3L369HBEu1K71DLrmIm6QRuD4teAP6uxzN0XlwMW+f+svxF3bfF3iOcPpR5ok0ORg==","signatures":[{"sig":"MEUCIDRBWpYDPqwK3C8zLumCd9RofVwqnh1qm9OqRKTTECnGAiEA2EC9CPRAY/gR/HRyk0YlIr5RcdahHWcyujn6FDpnRQQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH4u5ByEv3xTVzu3SYe3dPoq3ZbNGKibGVHsbyw+vvrxAiBrqJlgGDL0f/dgWQuYhNebG2W3/J5bhlMYgOSxjEK8gA=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@comers%2fn8n-nodes-comers@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":122328},"name":"@comers/n8n-nodes-comers","author":{"name":"Damian Nosowicz","email":"damian@nzt.li"},"gitHead":"fe0b3d9eb421f72f9ff793cee6eeef8797b353a0","license":"MIT","scripts":{"dev":"n8n-node dev","lint":"n8n-node lint","scan":"node scripts/scan-package.mjs","test":"vitest run","build":"n8n-node build","release":"node scripts/release.mjs","lint:fix":"n8n-node lint --fix","check:tag":"node scripts/release-tag.mjs","pack:check":"node scripts/check-tarball.mjs","test:watch":"vitest","build:watch":"tsc --watch","prepublishOnly":"node scripts/prepublish.mjs"},"version":"0.3.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"cf7e7509-a05b-41c6-931f-46b8b7bddbc7"}},"homepage":"https://github.com/rocketdeploy-dev/n8n-nodes-comers","keywords":["n8n-community-node-package","comers","webhook","trigger","events"],"release-it":{"git":{"tag":true,"push":true,"commit":true,"tagName":"${version}","commitMessage":"chore: release ${version}","requireBranch":"main","tagAnnotation":"Release ${version}","requireCommits":true,"requireUpstream":true,"requireCleanWorkingDir":true},"npm":{"publish":false},"hooks":{"after:bump":"node scripts/finalize-changelog.mjs","before:bump":"node scripts/finalize-changelog.mjs --check-version ${version}","before:init":["node scripts/finalize-changelog.mjs --check","npm run lint","npm run build","npm test","npm run scan","npm run pack:check"]},"github":{"release":false}},"repository":{"url":"git+https://github.com/rocketdeploy-dev/n8n-nodes-comers.git","type":"git"},"_npmVersion":"12.0.2","description":"Receive Comers domain events in n8n, with automatic subscription management","directories":{},"maintainers":[{"name":"rocketdeploy.dev","email":"damian@nzt.li"}],"_nodeVersion":"24.21.0","_hasShrinkwrap":false,"devDependencies":{"yaml":"^2.9.0","eslint":"9.32.0","vitest":"^4.1.9","prettier":"3.6.2","release-it":"^19.0.4","typescript":"5.9.2","@n8n/node-cli":"^0.47.2","@n8n/scan-community-package":"^0.35.0"},"peerDependencies":{"n8n-workflow":"*"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/n8n-nodes-comers_0.3.1_1790781251521_0.19130528794288226"}}},"time":{"created":"2026-09-11T12:01:50.312Z","modified":"2026-09-30T15:14:12.039Z","0.1.0":"2026-09-11T12:01:50.652Z","0.1.1":"2026-09-12T09:53:57.019Z","0.2.0":"2026-09-22T16:43:38.677Z","0.3.0":"2026-09-23T21:37:04.273Z","0.3.1":"2026-09-30T15:14:11.603Z"},"bugs":{"url":"https://github.com/rocketdeploy-dev/n8n-nodes-comers/issues"},"author":{"name":"Damian Nosowicz","email":"damian@nzt.li"},"license":"MIT","homepage":"https://github.com/rocketdeploy-dev/n8n-nodes-comers","keywords":["n8n-community-node-package","comers","webhook","trigger","events"],"repository":{"url":"git+https://github.com/rocketdeploy-dev/n8n-nodes-comers.git","type":"git"},"description":"Receive Comers domain events in n8n, with automatic subscription management","maintainers":[{"name":"rocketdeploy.dev","email":"damian@nzt.li"}],"readme":"# n8n-nodes-comers\n\nReceive [Comers](https://github.com/rocketdeploy-dev) domain events in\n[n8n](https://n8n.io). Publishing a workflow subscribes it in Comers;\nunpublishing it archives the subscription.\n\nThis package contains one trigger node and one credential:\n\n| | |\n| --- | --- |\n| **Comers Trigger** | Creates the workflow's own Comers event subscription when the workflow is published, verifies every delivery against Comers' public keys before the workflow runs, and archives the subscription when the workflow is unpublished or deleted. |\n| **Comers API** | A Comers machine integration: the Comers URL, a client ID and a client secret. |\n\nThe one-time API client ID and client secret are copied from Comers into the\nencrypted n8n credential. No webhook URL or delivery-signing secret is copied:\nthe node manages its webhook subscriptions and verifies deliveries with public\nkeys. A future zero-copy authorization flow is a separate enhancement.\n\n## Installation\n\nIn n8n, go to **Settings → Community nodes → Install** and enter\n`@comers/n8n-nodes-comers`.\n\n## Setting up\n\n### 1. Create a machine integration in Comers\n\nIn Comers, create an API integration for this n8n and grant it the scope\n**`comers.core.events.subscriptions.manage-own`**. Comers shows the client ID\nand the client secret; the secret is shown once.\n\n### 2. Add the credential\n\nIn n8n, create a **Comers API** credential:\n\n| Field | Value |\n| --- | --- |\n| **Comers URL** | The public HTTPS origin of your Comers installation, without a path |\n| **Client ID** | The integration's client ID |\n| **Client Secret** | The integration's client secret |\n\nThe credential test asks Comers for a token with exactly the scope above, so it\ntells you apart a wrong client ID or secret (*Comers did not accept this client\nID and secret*) and a missing scope (*… needs the scope\ncomers.core.events.subscriptions.manage-own*).\n\n### 3. Add the trigger and choose events\n\nAdd **Comers Trigger**, pick the credential and choose events from **Events**.\nThe node loads the versioned choices from the Comers event catalog and stores\neach selection as one `eventKey@eventVersion` value. There is no fixed list in\nthe package: an event Comers adds later appears without a new node release.\n\n**Subscription Name** is optional; by default it is the workflow and node\nnames. The node always appends a short identifier, `[n8n <id>]`, which it uses\nto find its own subscription again.\n\n### 4. Publish the workflow\n\nPublishing creates the subscription in Comers for the workflow's **production**\nwebhook URL. Unpublishing or deleting the workflow archives it; publishing\nagain creates a new one. Changing the events or the name and publishing again\nupdates the existing subscription.\n\n\"Listen for test event\" in the editor creates a temporary Comers subscription\nfor n8n's `webhook-test` URL. It is separate from the production subscription,\nuses the same `jws-es256-v1` verification, and expires after ten minutes. After\nthe first verified test delivery the node also attempts to archive it\nimmediately; the server-side expiry is the guaranteed cleanup backstop because\nn8n does not guarantee a callback when listening stops. Publishing creates a\nseparate, non-expiring production subscription for the `webhook` URL, and its\ndeliveries appear in the executions list.\n\nn8n registers the webhook just after the workflow is published. If Comers\ncannot be reached or refuses (for example because the integration lacks the\nscope), n8n shows the error on the workflow and the subscription is not\ncreated; fix the cause and publish again.\n\n## What the node stores\n\nThe node keeps, in the workflow's static data, only what it needs to find and\nverify its subscription — none of it is secret:\n\n| Key | |\n| --- | --- |\n| `schemaVersion` | The layout of this state |\n| `production` | Non-secret registration slot for the published workflow |\n| `test` | Separate non-secret registration slot for editor listening |\n\nEach slot may contain `registrationId`, `subscriptionId`, `jwksUri`,\n`signatureProfile` and `organizationId`. Keeping two slots prevents a test\nlistener from adopting, updating or archiving the production subscription.\n\nn8n stores static data unencrypted and includes it in workflow exports, which\nis exactly why nothing secret goes there. The client secret stays in the\nencrypted credential and is only ever sent to the Comers token endpoint. The\naccess token lives in memory until shortly before it expires; after a restart\nthe node simply asks for a new one. Comers' public keys are cached in memory\nfor as long as Comers says they may be.\n\n## The lifecycle, exactly\n\n**Publish** — n8n asks the node whether its subscription exists:\n\n- with a recorded `subscriptionId`, the node reads it. Found and pointing at\n  this workflow's URL: it exists (its name and events are brought up to date).\n  Archived, or pointing at another URL: it is retired and a new one is created.\n  Not found (404): the stale ID is forgotten.\n- with no usable ID, the node looks through the integration's own\n  subscriptions for exactly one that is not archived, uses this workflow's URL\n  and carries this node's identifier. That recovers a subscription whose\n  creation succeeded in Comers but whose answer never reached n8n, instead of\n  creating a second one. Two matches are an error, never a guess, and a\n  subscription that merely looks similar is never adopted.\n- any other answer from Comers is an error. It is never read as \"does not\n  exist\", because that would create a duplicate.\n\nWhen nothing exists, the node creates a `jws-es256-v1` subscription and records\nit.\n\n**Unpublish or delete** — the node archives exactly the recorded subscription.\nThe state is cleared only when Comers confirms (204) or no longer knows it\n(404); on any other answer it is kept, so n8n can retry the cleanup.\n\n## What the workflow receives\n\nA verified delivery produces exactly one item, with two keys, all of it covered\nby the signature:\n\n- **`event`** — the Comers event envelope, exactly as signed. Nothing is\n  renamed, removed, added or overwritten, and fields Comers adds later come\n  through untouched.\n- **`delivery`** — the delivery it came in.\n\n```json\n{\n  \"event\": {\n    \"specVersion\": \"comers.v1\",\n    \"eventId\": \"0199c3f0-1a2b-7c3d-8e4f-000000000001\",\n    \"eventKey\": \"comers.core.support.case.opened\",\n    \"eventVersion\": 1,\n    \"sequence\": \"9007199254740993\",\n    \"occurredAt\": \"2026-09-22T07:05:30.000Z\",\n    \"producer\": \"comers-core-support\",\n    \"scope\": {\n      \"organizationId\": \"0199c3f0-1a2b-7c3d-8e4f-00000000000a\",\n      \"sellerId\": null,\n      \"sellerStoreId\": null\n    },\n    \"subject\": { \"type\": \"support_case\", \"id\": \"0199c3f0-1a2b-7c3d-8e4f-00000000000b\" },\n    \"correlationId\": null,\n    \"data\": { \"priority\": \"high\" }\n  },\n  \"delivery\": {\n    \"subscriptionId\": \"0199c3f0-1a2b-7c3d-8e4f-000000000002\",\n    \"deliveryId\": \"0199c3f0-1a2b-7c3d-8e4f-000000000003\",\n    \"deliveryAttempt\": 1,\n    \"timestamp\": 1788259530\n  }\n}\n```\n\n`sequence` is a decimal string because it is a 64-bit counter. The JWS itself,\nthe token and the credential never appear in the output or in the logs.\n\n### Write idempotent workflows\n\nDelivery is **at-least-once**: the same `event.eventId` can arrive more than\nonce — after a timeout, a retry or a replay from Comers. `deliveryAttempt`\ncounts from 1 within a run and starts at 1 again after a replay, so it is not an\nidentifier. Deduplicate on **`event.eventId`**. The node keeps no record of what\nit has seen.\n\n## How a delivery is verified\n\nComers sends every delivery as an RFC 7515 JWS in flattened JSON serialization\n(`Content-Type: application/json`):\n`{\"protected\": …, \"payload\": …, \"signature\": …}`, signed with ES256 by a key\nthat belongs to your Comers installation — not to this workflow, and never\nshared with anyone. Before the workflow runs, the node:\n\n1. requires exactly `protected`, `payload` and `signature` — no unprotected\n   header;\n2. requires the protected header to be exactly `alg: ES256`,\n   `typ: comers-delivery+jws` and a `kid` of the form `v<version>.<thumbprint>`.\n   `none`, HMAC and every other algorithm are refused before a key is chosen;\n3. finds the `kid` among the keys Comers publishes at\n   `<Comers URL>/core/api/v1/event-delivery-keys` — only that origin and that\n   path, as recorded at registration. Every key must be a well-formed public\n   P-256 key whose `kid` is its own RFC 7638 thumbprint. The key set is cached\n   for its `Cache-Control: max-age`. An unknown `kid` triggers one refresh (at\n   most one every 10 seconds), then the delivery is refused;\n4. verifies the signature over the exact bytes received;\n5. only then decodes the payload, and requires it to be signed for **this**\n   subscription and this organization, with a timestamp within **300 seconds**\n   of the n8n clock, and a valid Comers envelope.\n\nKeep the n8n clock synchronised (NTP): the timestamp check is what stops a\ncaptured delivery being replayed later.\n\n### Response codes, and what Comers does with them\n\n| Status | Body | Meaning | Comers |\n| --- | --- | --- | --- |\n| `200` | — | Verified; the workflow runs | delivered |\n| `400` | `not_flattened_jws`, `malformed_envelope` | Not a Comers delivery | dead letter, no retry |\n| `401` | `algorithm`, `protected_header`, `unknown_kid`, `signature`, `payload`, `payload_shape`, `malformed_delivery`, `other_subscription`, `other_organization`, `stale_timestamp`, `unsupported_spec_version`, `not_registered` | Refused | dead letter, no retry |\n| `503` | `keys_unavailable` | The public keys could not be fetched | retried |\n| `500` | `internal_error` | Unexpected failure | retried |\n\nA `200` means n8n accepted the event, not that the workflow succeeded.\n\n## Credentials\n\n**Comers API** holds the Comers URL, the client ID and the client secret (as a\npassword field). n8n applies the client secret only to the token request, as\nHTTP Basic client authentication (`client_secret_basic`); every other call\ncarries a short-lived access token. When Comers rejects a cached token, the\nnode fetches a new one once and repeats the call once.\n\nRotating the client secret in Comers means updating the credential; the\nsubscription and its deliveries are unaffected, because no delivery secret\nexists.\n\n## Compatibility\n\n**Tested with n8n 2.40.5** — the built package loaded as a custom extension and\nwas exercised against the Comers API contract: the credential test, dynamic\ncatalog, separate test and production subscriptions, signed deliveries running\nthe workflow, test cleanup, and production unpublish cleanup. Version `0.3.0`\nwas then exercised against the production Comers deployment. Official n8n\ncommunity-node verification remains pending the Creator Portal video review.\n\nRequires a Comers installation with machine access and `jws-es256-v1` delivery\n(machine-credentials M7A). The package has no runtime dependencies. It reads no\nenvironment variables and touches no files.\n\n## Development\n\n```sh\nnpm install\nnpm run dev          # n8n with this node loaded, on http://localhost:5678\nnpm test             # the verifier, the envelope reader and the node\nnpm run lint\nnpm run build\nnpm run scan         # the official n8n community-package scanner\nnpm run pack:check   # what publishing would upload\n```\n\n`test/fixtures/contract-vectors.json` holds deliveries signed by the real Core\nEvents signer. They are static fixtures: this package has no dependency, at\nbuild time or run time, on the Comers repositories. The file records how it was\nproduced, so it can be regenerated if the signing contract ever moves.\n\n### Contributing\n\n- Use the `n8n-node` CLI for building, linting and dev mode. It is what n8n\n  itself checks against, and `npm run lint` runs in strict mode, so a change\n  that passes locally passes verification.\n- Keep `dependencies` empty. Verified community nodes may not have runtime\n  dependencies, and `node:crypto` is the only module this node needs.\n- Do not read environment variables or touch the filesystem. Both are\n  disallowed, and the linter enforces it.\n- If you change the version, update `CHANGELOG.md` in the same commit.\n\n### Releasing\n\nA release has two halves. Something creates a tag; the publish workflow reacts\nto it. The workflow never versions, commits or tags — the only thing that\nchanges the repository is the half that runs before it.\n\nPushing a release tag starts `.github/workflows/publish.yml`, which re-runs\nlint, build, tests, the scanner and the tarball check, confirms the tag names\nthe version in `package.json`, and publishes with npm provenance over Trusted\nPublishing. It reads no secret — there is none to read.\n\nPublishing from a developer machine is refused outright — a package published\nthat way carries no provenance attestation and could never become a verified\ncommunity node, while still burning the version number.\n\n#### Tag format\n\nTags are the bare version, with no `v` prefix and no build metadata:\n\n| | |\n| --- | --- |\n| Accepted | `0.1.0`, `1.2.3`, `2.0.0-rc.1` |\n| Refused | `v0.1.0`, `1.2.3+build.4`, a tag naming a different version than `package.json`, anything that is not a version |\n\nThe format is pinned by the `release-it` block in `package.json` rather than\ninferred from whatever tags already exist, and `npm run check:tag` enforces it.\n\nThe workflow's own tag filter is deliberately the looser of the two — a GitHub\nref filter treats `+` as a quantifier rather than a literal, so it cannot\nexclude a tag for carrying build metadata. Every tag the check accepts starts a\nrun, so a valid release is never silently ignored; a malformed one that slips\npast the filter fails the check loudly instead.\n\n#### Every release\n\n```sh\nnpm run release -- 0.1.1\n```\n\nThe version is mandatory: `npm run release` with nothing after it refuses to\nrun rather than choosing a patch bump for you.\n\nThat command runs the same checks the publish pipeline does — lint, build,\ntests, the n8n scanner and the tarball check — then sets the version in\n`package.json` and `package-lock.json`, moves whatever is written under\n`## Unreleased` in `CHANGELOG.md` into a dated `## 0.1.1 — YYYY-MM-DD` section,\nmakes one commit `chore: release 0.1.1`, creates the annotated tag `0.1.1`, and\npushes both.\n\nReleased sections are carried over byte for byte. Code blocks, indentation,\nblank runs and trailing spaces in earlier entries are left exactly as they were\nwritten: they are a published record, not this script's to reflow.\n\n**It never publishes.** Pushing the tag starts\n`.github/workflows/publish.yml`, which re-runs every check against the tagged\ncommit, confirms the tag names the version in `package.json`, and publishes over\nTrusted Publishing — no token, no secret, nothing to rotate. The local command\nneeds no npm credential and no GitHub token.\n\nIf anything is not right — uncommitted changes to tracked files, the wrong\nbranch, no upstream, nothing new since the last tag, an empty `## Unreleased`,\nor a section for that version already present — the release stops before any\ncommit or tag exists.\n\nEvery predictable refusal happens before a single file is written — the\nversion-specific ones too, since release-it makes the version available to the\n`before:bump` hook. A refused release leaves the working tree exactly as it was.\n\nOne thing worth knowing: \"clean working tree\" means no uncommitted changes to\n**tracked** files. Untracked files do not stop a release, and cannot reach it\neither — the release commit stages only tracked changes, and `npm run pack:check`\nasserts what the tarball contains.\n\n`n8n-node release` is not used here. It passes\n`--hooks.after:bump=\"npx auto-changelog -p\"` as a command-line argument, and in\nrelease-it a command-line argument overrides configuration, so a project cannot\nopt out of it. Rebuilding the changelog from commit subjects is a sensible\ndefault for a generated changelog; this one is written by hand and says why\nthings changed, so the project drives release-it itself and keeps the file. The\nsettings live in the `release-it` block of `package.json`,\n`scripts/release.mjs` is the wrapper that makes the version mandatory, and\n`scripts/finalize-changelog.mjs` is the hook that checks and dates the notes.\n\n#### How 0.1.0 came to exist\n\nPublished on 11 September 2026, and the only release that did not follow the\nprocedure above.\n\nnpm Trusted Publishing is configured *on a package*, so there was nothing to\nconfigure until the package existed. 0.1.0 was therefore published from this\nsame workflow, on a GitHub-hosted runner and with `--provenance`, but\nauthenticated with a single-use granular token rather than OIDC.\n\nThat token has since been revoked and the GitHub secret holding it deleted, and\nthe Trusted Publisher is in place. Nothing in this repository reads a\ncredential any more, and no release will need one again — which is why the\nbootstrap path is gone from `publish.yml` rather than kept around disabled.\n\n## Licence\n\n[MIT](LICENSE)\n","readmeFilename":"README.md"}