
Security News
OpenCode User Hit with Hostile Backlash After Reporting Free AI Loophole
An OpenCode user faced harassment after reporting a free AI usage loophole, drawing a bitter backlash in a GitHub thread that has since been deleted.
convex-webhook-receiver
Advanced tools
Convex component for inbound webhook ingestion with signature verification, deduplication, and retry
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.
X-GitHub-Delivery) with optional caller-supplied keys200 OK immediately; your handler runs in the backgroundnpm install convex-webhook-receiver
// 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
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...
},
})
// 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
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.
| Provider | Verification method | Auto dedup header |
|---|---|---|
github | HMAC-SHA256 (X-Hub-Signature-256) | X-GitHub-Delivery |
stripe | HMAC-SHA256 + timestamp tolerance (Stripe-Signature) | — |
slack | HMAC-SHA256 + timestamp tolerance (X-Slack-Signature) | — |
twilio | HMAC-SHA1 over URL (X-Twilio-Signature) | — |
shopify | HMAC-SHA256 base64 (X-Shopify-Hmac-Sha256) | — |
linear | HMAC-SHA256 hex (linear-signature) | — |
discord | Ed25519 (X-Signature-Ed25519) | — |
generic | HMAC-SHA256 hex (X-Webhook-Signature) | — |
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,
})
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',
})
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 })
},
})
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),
})
A working example app with GitHub, Stripe, and Slack endpoints is in apps/example.
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
}
Apache-2.0
FAQs
Convex component for inbound webhook ingestion with signature verification, deduplication, and retry
The npm package convex-webhook-receiver receives a total of 1,979 weekly downloads. As such, convex-webhook-receiver popularity was classified as popular.
We found that convex-webhook-receiver demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
An OpenCode user faced harassment after reporting a free AI usage loophole, drawing a bitter backlash in a GitHub thread that has since been deleted.

Threat Research
/Security News
319 browser extensions installed by 13.4 million users hijack searches, capture queries, and collect referral payments from Yahoo and Bing.

Threat Research
Malicious Firefox extensions mimic Phantom, Jupiter, and Bitget wallets to steal recovery phrases and private keys, with one fetching its payload remotely.