Invect
Plugins

Webhooks

Receive and process incoming webhooks from external services.

@invect/webhooks adds webhook ingestion to Invect. Register a trigger.webhook node in a flow and the plugin will handle receiving HTTP events from external services — with built-in signature verification, rate limiting, and deduplication.

Installation

npm install @invect/webhooks

Add the plugin to Invect

import { createInvectRouter } from '@invect/express';
import { webhooksPlugin } from '@invect/webhooks';

app.use('/invect', createInvectRouter({
  baseDatabaseConfig: {
    type: 'sqlite',
    connectionString: process.env.DATABASE_URL || 'file:./dev.db',
    id: 'main',
  },
  plugins: [
    webhooksPlugin({
      webhookBaseUrl: 'https://example.com/invect',
    }),
  ],
}));
InvectModule.forRoot({
  baseDatabaseConfig: {
    type: 'postgresql',
    connectionString: process.env.DATABASE_URL || 'postgresql://localhost:5432/invect',
    id: 'main',
  },
  plugins: [webhooksPlugin({ webhookBaseUrl: process.env.INVECT_URL })],
})
createInvectHandler({
  baseDatabaseConfig: {
    type: 'sqlite',
    connectionString: process.env.DATABASE_URL || 'file:./dev.db',
    id: 'main',
  },
  plugins: [webhooksPlugin({ webhookBaseUrl: process.env.INVECT_URL })],
})

Migrate the database

The plugin adds a webhook_triggers table. Generate and apply the schema:

npx invect-cli generate
npx invect-cli migrate --push

The Webhook Trigger node

In the flow editor, add a Webhook Trigger node (trigger.webhook) as the entry point of any flow you want to trigger via HTTP. The node exposes the incoming request body and headers as output, accessible to downstream nodes via templates:

{{ webhook_trigger.body.pull_request.title }}
{{ webhook_trigger.headers.x-github-event }}

Each webhook trigger gets a unique URL path and signing secret generated automatically.

Supported providers

Invect can verify signatures from these providers automatically:

ProviderSignature method
githubX-Hub-Signature-256 (HMAC-SHA256)
slackX-Slack-Signature (HMAC-SHA256)
stripeStripe-Signature (HMAC-SHA256)
linearX-Linear-Signature (HMAC-SHA256)
genericNo signature verification

Set the provider when creating a webhook trigger so that the plugin applies the correct verification logic.

Managing webhooks via API

Webhook triggers can be created and managed via the REST API:

MethodPathDescription
GET/plugins/webhooks/triggersList all webhook triggers
POST/plugins/webhooks/triggersCreate a webhook trigger
GET/plugins/webhooks/triggers/:idGet a webhook trigger
PUT/plugins/webhooks/triggers/:idUpdate a webhook trigger
DELETE/plugins/webhooks/triggers/:idDelete a webhook trigger
GET/plugins/webhooks/triggers/:id/infoGet the full webhook URL and secret
POST/plugins/webhooks/triggers/:id/testSend a test payload
POST/plugins/webhooks/receive/:webhookPathIngestion endpoint (public)

Creating a webhook trigger

// POST /plugins/webhooks/triggers
{
  "name": "GitHub PR Events",
  "provider": "github",
  "allowedMethods": "POST",
  "flowId": "my-flow-id"
}

Response includes the generated webhookPath, webhookSecret, and fullUrl (if webhookBaseUrl was configured).

The ingestion endpoint

External services should send payloads to:

POST /invect/plugins/webhooks/receive/:webhookPath

The plugin will:

  1. Check the rate limit
  2. Look up the webhook trigger by path
  3. Verify the signature (if the provider is not generic)
  4. Deduplicate the delivery using the provider's delivery ID header
  5. Record the payload and trigger the linked flow

Configuration

webhooksPlugin({
  // Base URL used to build full webhook URLs (e.g. "https://example.com/invect")
  webhookBaseUrl: process.env.INVECT_URL,

  // Rate limit: max requests per window (default: 60)
  rateLimitMaxRequests: 60,

  // Rate limit: window size in milliseconds (default: 60000)
  rateLimitWindowMs: 60_000,

  // Deduplication TTL in milliseconds (default: 86400000 — 24 hours)
  dedupTtlMs: 24 * 60 * 60 * 1000,
})