webhooks
|
Works with
---
name: webhooks
description: |
license: MIT
---
# Webhooks
## Receiving Webhooks
### Express (with signature verification)
```typescript
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['stripe-signature']!;
const event = stripe.webhooks.constructEvent(req.body, signature, WEBHOOK_SECRET);
// Idempotency: check if already processed
const existing = await db.webhookEvent.findUnique({ where: { eventId: event.id } });
if (existing) return res.json({ received: true });
// Process
switch (event.type) {
case 'checkout.session.completed':
await fulfillOrder(event.data.object);
break;
}
// Mark as processed
await db.webhookEvent.create({ data: { eventId: event.id, type: event.type } });
res.json({ received: true });
});
```
### Generic HMAC Verification
```typescript
import { createHmac, timingSafeEqual } from 'crypto';
function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret).update(payload).digest('hex');
return timingSafeEqual(Buffer.from(signature), Buffer.from(`sha256=${expected}`));
}
app.post('/webhooks/github', express.raw({ type: '*/*' }), (req, res) => {
const sig = req.headers['x-hub-signature-256'] as string;
if (!verifyWebhookSignature(req.body.toString(), sig, GITHUB_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process event...
res.status(200).json({ received: true });
});
```
## Sending Webhooks
```typescript
class WebhookDispatcher {
async dispatch(url: string, event: WebhookEvent, secret: string) {
const payload = JSON.stringify(event);
const signature = createHmac('sha256', secret).update(payload).digest('hex');
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': `sha256=${signature}`,
'X-Webhook-Id': event.id,
'X-Webhook-Timestamp': new Date().toISOString(),
},
body: payload,
signal: AbortSignal.timeout(10000),
});
if (!response.ok) throw new WebhookDeliveryError(response.status);
}
}
```
### Retry with Exponential Backoff
```typescript
// Use job queue for reliable delivery
await webhookQueue.add('deliver', {
url: subscription.url,
event: { id: uuid(), type: 'order.created', data: order },
secret: subscription.secret,
}, {
attempts: 5,
backoff: { type: 'exponential', delay: 60000 }, // 1m, 2m, 4m, 8m, 16m
});
```
## Webhook Event Schema
```typescript
interface WebhookEvent {
id: string; // Unique event ID (for idempotency)
type: string; // 'order.created', 'user.deleted'
timestamp: string; // ISO 8601
data: unknown; // Event payload
version: string; // API version
}
```
## Anti-Patterns
| Anti-Pattern | Fix |
|--------------|-----|
| No signature verification | Always verify HMAC signature |
| Processing before responding 200 | Respond 200 immediately, process async |
| No idempotency check | Store processed event IDs |
| Using parsed body for verification | Use raw body for signature check |
| No retry on send failures | Use job queue with exponential backoff |
| Synchronous webhook delivery | Dispatch via background job queue |
## Production Checklist
- [ ] HMAC signature verification on receive
- [ ] Raw body parsing (not JSON-parsed) for signature
- [ ] Idempotency: deduplicate by event ID
- [ ] Respond 200 before processing
- [ ] Retry with exponential backoff on send
- [ ] Dead letter queue for failed deliveries
- [ ] Webhook event log for debuggingMore API Design skills
lark-event
larksuite/cli
Lark/Feishu real-time event listening / subscribing / consuming: stream events as NDJSON via `lark-cli event consume <EventKey>` (covers IM messages/reactions/chat changes, Approval status changes, Task updates, VC meeting started/joined/ended, Minutes generated, Whiteboard updated, etc.). Use for Lark bots, real-time message processing, long-running subscribers, streaming webhook/push handlers. Supports `--max-events` / `--timeout` bounded runs and a stderr ready-marker contract — designed for AI agents running as subprocesses.
lark-contact
larksuite/cli
飞书 / Lark 通讯录:按姓名 / 邮箱解析成 open_id,或按 open_id 反查姓名 / 部门 / 邮箱 / 联系方式 / 个人状态 / 签名,以及按关键词搜索当前用户可见的机器人 / 智能体(agent)。当用户提到一个名字要下一步发消息 / 排日程,或拿到 open_id 想查具体信息时使用。不负责部门树遍历、按部门列员工、组织架构图,这类需求走原生 OpenAPI。
lark-openapi-explorer
larksuite/cli
飞书/Lark 原生 OpenAPI 探索:从官方文档库中挖掘未经 CLI 封装的原生 OpenAPI 接口。当用户的需求无法被现有 lark-* skill 或 lark-cli 已注册命令满足,需要查找并调用原生飞书 OpenAPI 时使用。

