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/webhooksAdd 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 --pushThe 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:
| Provider | Signature method |
|---|---|
github | X-Hub-Signature-256 (HMAC-SHA256) |
slack | X-Slack-Signature (HMAC-SHA256) |
stripe | Stripe-Signature (HMAC-SHA256) |
linear | X-Linear-Signature (HMAC-SHA256) |
generic | No 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:
| Method | Path | Description |
|---|---|---|
GET | /plugins/webhooks/triggers | List all webhook triggers |
POST | /plugins/webhooks/triggers | Create a webhook trigger |
GET | /plugins/webhooks/triggers/:id | Get a webhook trigger |
PUT | /plugins/webhooks/triggers/:id | Update a webhook trigger |
DELETE | /plugins/webhooks/triggers/:id | Delete a webhook trigger |
GET | /plugins/webhooks/triggers/:id/info | Get the full webhook URL and secret |
POST | /plugins/webhooks/triggers/:id/test | Send a test payload |
POST | /plugins/webhooks/receive/:webhookPath | Ingestion 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/:webhookPathThe plugin will:
- Check the rate limit
- Look up the webhook trigger by path
- Verify the signature (if the provider is not
generic) - Deduplicate the delivery using the provider's delivery ID header
- 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,
})