Skip to content

Find a component

Loading the directory…

↑ ↓ to select · Enter to open · Esc to closeBrowse all →
Contributor login

Webhook Receiver

Inbound webhook handling

Bijection source
git clone https://github.com/bijectionhq/webhook-receiver.git
cd webhook-receiver
git checkout 41aa9a2fd8ac767f9110816ea5c5147e6ff35318

Bijection repository access is required. Request access.

Description

Inbound webhook handling

Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.

Available to evaluate

Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.

Getting started

  1. Get the source at the revision above and read its package documentation.
  2. Check the required Bijection SDK version, dependencies, and external service credentials.
  3. Follow the package setup instructions and the guide to using components.
  4. Exercise the component in your deployment according to your project’s verification rules.

Open source

Maintained source: bijectionhq/webhook-receiver. Based on david-potgieter/convex-webhook-receiver. License: Apache-2.0. Consult the repository for license terms, notices, and contributor attribution.

From the upstream publisher

Convex component for inbound webhook ingestion with signature verification, deduplication, and retry

Explore the demo ↗

Upstream overview

From the upstream package documentation.

The Webhook Receiver component handles inbound webhook processing for Convex apps with signature verification, automatic deduplication, and reliable async processing. It supports 8+ providers (GitHub, Stripe, Slack, Twilio, etc.) with built-in retry logic and dead-letter queue management. Webhooks return 200 OK immediately while your handler functions process events in the background with exponential backoff on failures.

Use cases

From the upstream package documentation.

  • Process GitHub webhooks for CI/CD pipelines, issue tracking, or repository automation while ensuring delivery IDs prevent duplicate processing
  • Handle payment events from Stripe with signature verification and retry logic to ensure critical billing events are never lost
  • Ingest notifications from Slack or Discord for chat automation, moderation bots, or team workflow integrations
  • Process e-commerce webhooks from Shopify for order fulfillment, inventory updates, or customer lifecycle events
  • Build reliable integrations with any webhook provider using the generic HMAC-SHA256 verifier or custom verification functions

How it works

From the upstream package documentation.

The component registers as a Convex component in your convex.config.ts and provides an HTTP handler that you mount at webhook endpoints. When a webhook arrives, it immediately verifies the signature using provider-specific algorithms (HMAC-SHA256 for GitHub/Stripe, Ed25519 for Discord, etc.), deduplicates using delivery headers like X-GitHub-Delivery, and returns 200 OK to the sender.

Your handler function runs as an internal action in the background, receiving the raw body, headers, and provider info. The WebhookReceiver class manages the event lifecycle with configurable retry attempts (default 3) and exponential backoff capped at 30 minutes. Failed events that exhaust retries move to a dead-letter queue accessible via listDlq() and replayEvent() methods.

Configuration happens per-endpoint with options like maxAttempts, ttlDays, dedupKeyHeader, and custom verifier functions. The component stores event state in its own tables and provides query methods like listEvents() and getEvent() for monitoring webhook processing status.

Upstream package FAQ

Which webhook providers does this component support?

The Webhook Receiver component has built-in support for GitHub, Stripe, Slack, Twilio, Shopify, Linear, Discord, and generic HMAC-SHA256 webhooks. You can also pass a custom verifier function to handle any other signature scheme.

How does the retry mechanism work?

The component uses exponential backoff with a default of 3 attempts (1s, 2s, 4s intervals, capped at 30 minutes). You can configure maxAttempts globally or per-handler. Events that exhaust all retries are moved to a dead-letter queue for manual inspection and replay.

Can I customize the deduplication logic?

Yes, the Webhook Receiver component supports automatic deduplication via provider headers (like X-GitHub-Delivery), custom headers specified with dedupKeyHeader, or you can handle deduplication logic within your handler action for complete control.

How do I test webhooks with this component?

The Webhook Receiver component exports test helpers for use with convex-test. Register the component's schema and modules in your test setup, then you can test your webhook handlers with mock payloads and verify the processing behavior.

Use with agents and CLI

Give your coding agent the component’s source identity and setup guidance.

agent-prompt.md
Help me integrate Webhook Receiver with Bijection.

Read: https://bijection.com/components/webhook-receiver/SKILL.md

Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.

Upstream README.md

View repository ↗

Documentation from david-potgieter/convex-webhook-receiver, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.

convex-webhook-receiver

Reliable inbound webhook ingestion for Convex

Signature verification • Deduplication • Async processing • Retry • Dead-letter queue

Documentation • Supported Providers • API Reference • Example


A Convex component for reliable inbound webhook ingestion. Verifies signatures, deduplicates deliveries, queues events for async processing, retries failures with exponential backoff, and parks exhausted events in a dead-letter queue.

Features

  • Signature verification for GitHub, Stripe, Slack, Twilio, Shopify, Linear, Discord, and a generic HMAC-SHA256 escape hatch
  • Automatic deduplication via provider delivery IDs (e.g. X-GitHub-Delivery) with optional caller-supplied keys
  • Async processing — webhook endpoint returns 200 OK immediately; your handler runs in the background
  • Configurable retry with exponential backoff (default: 3 attempts, cap 30 minutes)
  • Dead-letter queue for events that exhaust retries
  • Manual replay for DLQ entries
  • TTL-based expiry for delivered events

Installation

npm install convex-webhook-receiver

Setup

1. Register the component

// convex/convex.config.ts
import { defineApp } from 'convex/server'
import webhookReceiver from 'convex-webhook-receiver/convex.config'

const app = defineApp()
app.use(webhookReceiver)
export default app

2. Create a handler action

Your handler receives the raw webhook payload and does whatever your app needs — storing to a table, triggering other functions, calling external APIs.

// convex/webhooks.ts
import { internalAction } from './_generated/server'
import { components } from './_generated/api'
import { v } from 'convex/values'
import { WebhookReceiver } from 'convex-webhook-receiver'

export const webhookReceiver = new WebhookReceiver(components.webhookReceiver)

export const handleGitHubEvent = internalAction({
  args: {
    provider: v.string(),
    rawBody: v.string(),
    headers: v.record(v.string(), v.string()),
  },
  handler: async (ctx, args) => {
    const eventType = args.headers['x-github-event']
    const payload = JSON.parse(args.rawBody)
    // do something with payload...
  },
})

3. Register the HTTP endpoint

// convex/http.ts
import { httpRouter } from 'convex/server'
import { internal } from './_generated/api'
import { webhookReceiver } from './webhooks'

const http = httpRouter()

http.route({
  path: '/webhooks/github',
  method: 'POST',
  handler: webhookReceiver.httpHandler({
    provider: 'github',
    verifierSecret: process.env.GITHUB_WEBHOOK_SECRET!,
    handler: internal.webhooks.handleGitHubEvent,
  }),
})

export default http

Supported providers

Any webhook source works — use generic for HMAC-SHA256 with a custom header, or pass your own verifier function for any other scheme. The built-in providers are convenience wrappers with the correct algorithm and header pre-configured.

ProviderVerification methodAuto dedup header
githubHMAC-SHA256 (X-Hub-Signature-256)X-GitHub-Delivery
stripeHMAC-SHA256 + timestamp tolerance (Stripe-Signature)—
slackHMAC-SHA256 + timestamp tolerance (X-Slack-Signature)—
twilioHMAC-SHA1 over URL (X-Twilio-Signature)—
shopifyHMAC-SHA256 base64 (X-Shopify-Hmac-Sha256)—
linearHMAC-SHA256 hex (linear-signature)—
discordEd25519 (X-Signature-Ed25519)—
genericHMAC-SHA256 hex (X-Webhook-Signature)—

Custom verifier

Pass your own verifier function to override signature checking entirely:

import type { Verifier } from 'convex-webhook-receiver'

const myVerifier: Verifier = async (request, rawBody, secret) => {
  // return true if valid
}

webhookReceiver.httpHandler({
  provider: 'generic',
  verifierSecret: process.env.WEBHOOK_SECRET!,
  handler: internal.webhooks.handleEvent,
  verifier: myVerifier,
})

Deduplication

GitHub webhooks are automatically deduplicated using the X-GitHub-Delivery header. For other providers, pass a dedupKeyHeader to specify which header carries a unique delivery ID, or handle deduplication in your handler.

webhookReceiver.httpHandler({
  provider: 'stripe',
  verifierSecret: process.env.STRIPE_WEBHOOK_SECRET!,
  handler: internal.webhooks.handleStripeEvent,
  dedupKeyHeader: 'stripe-event-id',
})

Retry and dead-letter queue

By default events are attempted up to 3 times with exponential backoff (1s, 2s, 4s… capped at 30 minutes). Configure per-handler or globally:

// Global default
const webhookReceiver = new WebhookReceiver(components.webhookReceiver, {
  maxAttempts: 5,
  ttlDays: 7,
})

// Per-handler override
webhookReceiver.httpHandler({
  provider: 'github',
  verifierSecret: process.env.GITHUB_WEBHOOK_SECRET!,
  handler: internal.webhooks.handleGitHubEvent,
  maxAttempts: 10,
  ttlDays: 30,
})

Events that exhaust all attempts are moved to the dead-letter queue. Query and replay them:

// convex/admin.ts
import { query, action } from './_generated/server'
import { components } from './_generated/api'
import { webhookReceiver } from './webhooks'
import { v } from 'convex/values'

export const listDlq = query({
  handler: async (ctx) => webhookReceiver.listDlq(ctx),
})

export const replayEvent = action({
  args: { eventId: v.string() },
  handler: async (ctx, { eventId }) => {
    await ctx.runAction(components.webhookReceiver.event.actions.replay, { eventId })
  },
})

API Reference

import { query } from './_generated/server'
import { v } from 'convex/values'
import { webhookReceiver } from './webhooks'

export const listEvents = query({
  handler: async (ctx) => webhookReceiver.listEvents(ctx),
})

export const getEvent = query({
  args: { eventId: v.string() },
  handler: async (ctx, { eventId }) => webhookReceiver.getEvent(ctx, eventId),
})

Example

A working example app with GitHub, Stripe, and Slack endpoints is in apps/example.

Testing

The package exports a test helper for use with convex-test:

// convex/webhooks.test.ts
import { convexTest } from 'convex-test'
import webhookReceiver from 'convex-webhook-receiver/test'
import schema from './schema'

const modules = import.meta.glob('./**/*.ts')

function makeT() {
  const t = convexTest(schema, modules)
  t.registerComponent('webhookReceiver', webhookReceiver.schema, webhookReceiver.modules)
  return t
}

License

Apache-2.0


Keywords