@intentsolutionsio/vercel-pack 1.0.0 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +67 -44
- package/package.json +4 -4
- package/skills/vercel-advanced-troubleshooting/SKILL.md +185 -195
- package/skills/vercel-advanced-troubleshooting/references/errors.md +11 -0
- package/skills/vercel-advanced-troubleshooting/references/evidence-collection-framework.md +34 -0
- package/skills/vercel-advanced-troubleshooting/references/examples.md +11 -0
- package/skills/vercel-advanced-troubleshooting/references/systematic-isolation.md +56 -0
- package/skills/vercel-advanced-troubleshooting/references/timing-analysis.md +35 -0
- package/skills/vercel-architecture-variants/SKILL.md +227 -216
- package/skills/vercel-architecture-variants/references/errors.md +11 -0
- package/skills/vercel-architecture-variants/references/examples.md +12 -0
- package/skills/vercel-architecture-variants/references/variant-a-monolith-(simple).md +44 -0
- package/skills/vercel-architecture-variants/references/variant-b-service-layer-(moderate).md +72 -0
- package/skills/vercel-architecture-variants/references/variant-c-microservice-(complex).md +81 -0
- package/skills/vercel-ci-integration/SKILL.md +183 -73
- package/skills/vercel-ci-integration/references/errors.md +10 -0
- package/skills/vercel-ci-integration/references/examples.md +36 -0
- package/skills/vercel-ci-integration/references/implementation.md +54 -0
- package/skills/vercel-common-errors/SKILL.md +164 -60
- package/skills/vercel-common-errors/references/errors.md +53 -0
- package/skills/vercel-common-errors/references/examples.md +23 -0
- package/skills/vercel-cost-tuning/SKILL.md +158 -145
- package/skills/vercel-cost-tuning/references/cost-estimation.md +34 -0
- package/skills/vercel-cost-tuning/references/cost-reduction-strategies.md +40 -0
- package/skills/vercel-cost-tuning/references/errors.md +11 -0
- package/skills/vercel-cost-tuning/references/examples.md +15 -0
- package/skills/vercel-data-handling/SKILL.md +202 -155
- package/skills/vercel-data-handling/references/errors.md +11 -0
- package/skills/vercel-data-handling/references/examples.md +27 -0
- package/skills/vercel-data-handling/references/implementation.md +223 -0
- package/skills/vercel-debug-bundle/SKILL.md +163 -67
- package/skills/vercel-debug-bundle/references/errors.md +12 -0
- package/skills/vercel-debug-bundle/references/examples.md +24 -0
- package/skills/vercel-debug-bundle/references/implementation.md +54 -0
- package/skills/vercel-deploy-integration/SKILL.md +163 -156
- package/skills/vercel-deploy-integration/references/errors.md +11 -0
- package/skills/vercel-deploy-integration/references/examples.md +21 -0
- package/skills/vercel-deploy-integration/references/google-cloud-run.md +36 -0
- package/skills/vercel-deploy-integration/references/vercel-deployment.md +35 -0
- package/skills/vercel-deploy-preview/SKILL.md +164 -39
- package/skills/vercel-edge-functions/SKILL.md +185 -37
- package/skills/vercel-enterprise-rbac/SKILL.md +185 -170
- package/skills/vercel-enterprise-rbac/references/errors.md +11 -0
- package/skills/vercel-enterprise-rbac/references/examples.md +12 -0
- package/skills/vercel-enterprise-rbac/references/role-implementation.md +33 -0
- package/skills/vercel-enterprise-rbac/references/sso-integration.md +35 -0
- package/skills/vercel-hello-world/SKILL.md +141 -55
- package/skills/vercel-incident-runbook/SKILL.md +186 -138
- package/skills/vercel-incident-runbook/references/errors.md +11 -0
- package/skills/vercel-incident-runbook/references/examples.md +10 -0
- package/skills/vercel-incident-runbook/references/immediate-actions-by-error-type.md +41 -0
- package/skills/vercel-install-auth/SKILL.md +130 -53
- package/skills/vercel-known-pitfalls/SKILL.md +235 -233
- package/skills/vercel-known-pitfalls/references/errors.md +11 -0
- package/skills/vercel-known-pitfalls/references/examples.md +12 -0
- package/skills/vercel-load-scale/SKILL.md +197 -204
- package/skills/vercel-load-scale/references/capacity-planning.md +47 -0
- package/skills/vercel-load-scale/references/errors.md +11 -0
- package/skills/vercel-load-scale/references/examples.md +26 -0
- package/skills/vercel-load-scale/references/load-testing-with-k6.md +59 -0
- package/skills/vercel-load-scale/references/scaling-patterns.md +65 -0
- package/skills/vercel-local-dev-loop/SKILL.md +159 -71
- package/skills/vercel-local-dev-loop/references/errors.md +11 -0
- package/skills/vercel-local-dev-loop/references/examples.md +21 -0
- package/skills/vercel-local-dev-loop/references/implementation.md +60 -0
- package/skills/vercel-migration-deep-dive/SKILL.md +202 -187
- package/skills/vercel-migration-deep-dive/references/errors.md +11 -0
- package/skills/vercel-migration-deep-dive/references/examples.md +12 -0
- package/skills/vercel-migration-deep-dive/references/implementation-plan.md +80 -0
- package/skills/vercel-migration-deep-dive/references/pre-migration-assessment.md +39 -0
- package/skills/vercel-multi-env-setup/SKILL.md +167 -164
- package/skills/vercel-multi-env-setup/references/configuration-structure.md +59 -0
- package/skills/vercel-multi-env-setup/references/errors.md +11 -0
- package/skills/vercel-multi-env-setup/references/examples.md +11 -0
- package/skills/vercel-observability/SKILL.md +205 -195
- package/skills/vercel-observability/references/alert-configuration.md +40 -0
- package/skills/vercel-observability/references/errors.md +11 -0
- package/skills/vercel-observability/references/examples.md +13 -0
- package/skills/vercel-observability/references/metrics-collection.md +65 -0
- package/skills/vercel-performance-tuning/SKILL.md +212 -156
- package/skills/vercel-performance-tuning/references/caching-strategy.md +49 -0
- package/skills/vercel-performance-tuning/references/errors.md +11 -0
- package/skills/vercel-performance-tuning/references/examples.md +13 -0
- package/skills/vercel-policy-guardrails/SKILL.md +276 -193
- package/skills/vercel-policy-guardrails/references/errors.md +11 -0
- package/skills/vercel-policy-guardrails/references/eslint-rules.md +46 -0
- package/skills/vercel-policy-guardrails/references/examples.md +10 -0
- package/skills/vercel-prod-checklist/SKILL.md +219 -94
- package/skills/vercel-prod-checklist/references/errors.md +11 -0
- package/skills/vercel-prod-checklist/references/examples.md +25 -0
- package/skills/vercel-prod-checklist/references/implementation.md +60 -0
- package/skills/vercel-rate-limits/SKILL.md +187 -100
- package/skills/vercel-rate-limits/references/errors.md +11 -0
- package/skills/vercel-rate-limits/references/examples.md +46 -0
- package/skills/vercel-rate-limits/references/implementation.md +66 -0
- package/skills/vercel-reference-architecture/SKILL.md +226 -180
- package/skills/vercel-reference-architecture/references/errors.md +11 -0
- package/skills/vercel-reference-architecture/references/examples.md +13 -0
- package/skills/vercel-reference-architecture/references/key-components.md +65 -0
- package/skills/vercel-reference-architecture/references/project-structure.md +40 -0
- package/skills/vercel-reliability-patterns/SKILL.md +272 -211
- package/skills/vercel-reliability-patterns/references/circuit-breaker.md +36 -0
- package/skills/vercel-reliability-patterns/references/dead-letter-queue.md +48 -0
- package/skills/vercel-reliability-patterns/references/errors.md +11 -0
- package/skills/vercel-reliability-patterns/references/examples.md +11 -0
- package/skills/vercel-reliability-patterns/references/idempotency-keys.md +36 -0
- package/skills/vercel-sdk-patterns/SKILL.md +264 -92
- package/skills/vercel-sdk-patterns/references/errors.md +11 -0
- package/skills/vercel-sdk-patterns/references/examples.md +45 -0
- package/skills/vercel-sdk-patterns/references/implementation.md +67 -0
- package/skills/vercel-security-basics/SKILL.md +186 -96
- package/skills/vercel-security-basics/references/errors.md +10 -0
- package/skills/vercel-security-basics/references/examples.md +70 -0
- package/skills/vercel-security-basics/references/implementation.md +39 -0
- package/skills/vercel-upgrade-migration/SKILL.md +167 -67
- package/skills/vercel-upgrade-migration/references/errors.md +10 -0
- package/skills/vercel-upgrade-migration/references/examples.md +51 -0
- package/skills/vercel-upgrade-migration/references/implementation.md +29 -0
- package/skills/vercel-webhooks-events/SKILL.md +208 -132
- package/skills/vercel-webhooks-events/references/errors.md +11 -0
- package/skills/vercel-webhooks-events/references/event-handler-pattern.md +37 -0
- package/skills/vercel-webhooks-events/references/examples.md +16 -0
- package/skills/vercel-webhooks-events/references/signature-verification.md +33 -0
|
@@ -1,199 +1,275 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vercel-webhooks-events
|
|
3
|
-
description:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
description: 'Implement Vercel webhook handling with signature verification and event
|
|
4
|
+
processing.
|
|
5
|
+
|
|
6
|
+
Use when setting up webhook endpoints, processing deployment events,
|
|
7
|
+
|
|
8
|
+
or building integrations that react to Vercel deployment lifecycle.
|
|
9
|
+
|
|
7
10
|
Trigger with phrases like "vercel webhook", "vercel events",
|
|
8
|
-
|
|
11
|
+
|
|
12
|
+
"vercel deployment.ready", "handle vercel events", "vercel webhook signature".
|
|
13
|
+
|
|
14
|
+
'
|
|
9
15
|
allowed-tools: Read, Write, Edit, Bash(curl:*)
|
|
10
16
|
version: 1.0.0
|
|
11
17
|
license: MIT
|
|
12
18
|
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
19
|
+
tags:
|
|
20
|
+
- saas
|
|
21
|
+
- vercel
|
|
22
|
+
- webhooks
|
|
23
|
+
- events
|
|
24
|
+
- integration
|
|
25
|
+
compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
|
|
13
26
|
---
|
|
14
|
-
|
|
15
27
|
# Vercel Webhooks & Events
|
|
16
28
|
|
|
17
29
|
## Overview
|
|
18
|
-
Securely handle Vercel webhooks with signature validation and replay protection.
|
|
19
30
|
|
|
20
|
-
|
|
21
|
-
- Vercel webhook secret configured
|
|
22
|
-
- HTTPS endpoint accessible from internet
|
|
23
|
-
- Understanding of cryptographic signatures
|
|
24
|
-
- Redis or database for idempotency (optional)
|
|
31
|
+
Handle Vercel webhook events (deployment.created, deployment.ready, deployment.error) with HMAC signature verification. Covers both integration webhooks (Vercel Marketplace) and project-level deploy hooks.
|
|
25
32
|
|
|
26
|
-
##
|
|
33
|
+
## Prerequisites
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
import crypto from 'crypto';
|
|
35
|
+
- HTTPS endpoint accessible from the internet
|
|
36
|
+
- Webhook secret from Vercel dashboard or integration settings
|
|
37
|
+
- `crypto` module for HMAC signature verification
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
## Instructions
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
app.post('/webhooks/vercel',
|
|
37
|
-
express.raw({ type: 'application/json' }),
|
|
38
|
-
async (req, res) => {
|
|
39
|
-
const signature = req.headers['x-vercel-signature'] as string;
|
|
40
|
-
const timestamp = req.headers['x-vercel-timestamp'] as string;
|
|
41
|
+
### Step 1: Register a Webhook
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
return res.status(401).json({ error: 'Invalid signature' });
|
|
44
|
-
}
|
|
43
|
+
In the Vercel dashboard:
|
|
45
44
|
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
1. Go to **Settings > Webhooks**
|
|
46
|
+
2. Add your endpoint URL (must be HTTPS)
|
|
47
|
+
3. Select events to subscribe to
|
|
48
|
+
4. Copy the webhook secret for signature verification
|
|
48
49
|
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
```
|
|
50
|
+
Or for Integration webhooks, configure in the Integration Console at `vercel.com/dashboard/integrations`.
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
### Step 2: Verify Webhook Signature
|
|
55
53
|
|
|
56
54
|
```typescript
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
timestamp: string
|
|
61
|
-
): boolean {
|
|
62
|
-
const secret = process.env.VERCEL_WEBHOOK_SECRET!;
|
|
63
|
-
|
|
64
|
-
// Reject old timestamps (replay attack protection)
|
|
65
|
-
const timestampAge = Date.now() - parseInt(timestamp) * 1000;
|
|
66
|
-
if (timestampAge > 300000) { // 5 minutes
|
|
67
|
-
console.error('Webhook timestamp too old');
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
55
|
+
// api/webhooks/vercel.ts
|
|
56
|
+
import type { VercelRequest, VercelResponse } from '@vercel/node';
|
|
57
|
+
import crypto from 'crypto';
|
|
70
58
|
|
|
71
|
-
|
|
72
|
-
|
|
59
|
+
const WEBHOOK_SECRET = process.env.VERCEL_WEBHOOK_SECRET!;
|
|
60
|
+
|
|
61
|
+
function verifySignature(body: string, signature: string): boolean {
|
|
73
62
|
const expectedSignature = crypto
|
|
74
|
-
.createHmac('
|
|
75
|
-
.update(
|
|
63
|
+
.createHmac('sha1', WEBHOOK_SECRET)
|
|
64
|
+
.update(body)
|
|
76
65
|
.digest('hex');
|
|
77
|
-
|
|
78
|
-
// Timing-safe comparison
|
|
79
66
|
return crypto.timingSafeEqual(
|
|
80
67
|
Buffer.from(signature),
|
|
81
68
|
Buffer.from(expectedSignature)
|
|
82
69
|
);
|
|
83
70
|
}
|
|
71
|
+
|
|
72
|
+
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
|
73
|
+
if (req.method !== 'POST') {
|
|
74
|
+
return res.status(405).json({ error: 'Method not allowed' });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Get raw body for signature verification
|
|
78
|
+
const rawBody = JSON.stringify(req.body);
|
|
79
|
+
const signature = req.headers['x-vercel-signature'] as string;
|
|
80
|
+
|
|
81
|
+
if (!signature || !verifySignature(rawBody, signature)) {
|
|
82
|
+
return res.status(401).json({ error: 'Invalid signature' });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Process the event
|
|
86
|
+
const event = req.body;
|
|
87
|
+
await handleEvent(event);
|
|
88
|
+
|
|
89
|
+
res.status(200).json({ received: true });
|
|
90
|
+
}
|
|
84
91
|
```
|
|
85
92
|
|
|
86
|
-
|
|
93
|
+
### Step 3: Handle Deployment Events
|
|
87
94
|
|
|
88
95
|
```typescript
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
interface VercelEvent {
|
|
96
|
+
// lib/webhook-handlers.ts
|
|
97
|
+
interface VercelWebhookEvent {
|
|
92
98
|
id: string;
|
|
93
|
-
type:
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
type: string;
|
|
100
|
+
createdAt: number;
|
|
101
|
+
payload: {
|
|
102
|
+
deployment: {
|
|
103
|
+
id: string;
|
|
104
|
+
name: string;
|
|
105
|
+
url: string;
|
|
106
|
+
meta: Record<string, string>;
|
|
107
|
+
};
|
|
108
|
+
project: {
|
|
109
|
+
id: string;
|
|
110
|
+
name: string;
|
|
111
|
+
};
|
|
112
|
+
target: 'production' | 'preview' | null;
|
|
113
|
+
user: { id: string; email: string; username: string };
|
|
114
|
+
};
|
|
96
115
|
}
|
|
97
116
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
117
|
+
async function handleEvent(event: VercelWebhookEvent): Promise<void> {
|
|
118
|
+
switch (event.type) {
|
|
119
|
+
case 'deployment.created':
|
|
120
|
+
console.log(`Deployment started: ${event.payload.deployment.url}`);
|
|
121
|
+
// Notify Slack, update status board, etc.
|
|
122
|
+
break;
|
|
123
|
+
|
|
124
|
+
case 'deployment.ready':
|
|
125
|
+
console.log(`Deployment ready: ${event.payload.deployment.url}`);
|
|
126
|
+
// Run smoke tests against the deployment URL
|
|
127
|
+
// Notify team of successful deploy
|
|
128
|
+
if (event.payload.target === 'production') {
|
|
129
|
+
await notifyProductionDeploy(event);
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
case 'deployment.error':
|
|
134
|
+
console.error(`Deployment failed: ${event.payload.deployment.id}`);
|
|
135
|
+
// Alert on-call engineer
|
|
136
|
+
// Create incident ticket
|
|
137
|
+
await notifyDeploymentError(event);
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case 'deployment.canceled':
|
|
141
|
+
console.log(`Deployment canceled: ${event.payload.deployment.id}`);
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case 'project.created':
|
|
145
|
+
console.log(`New project: ${event.payload.project.name}`);
|
|
146
|
+
break;
|
|
147
|
+
|
|
148
|
+
case 'project.removed':
|
|
149
|
+
console.log(`Project removed: ${event.payload.project.name}`);
|
|
150
|
+
break;
|
|
151
|
+
|
|
152
|
+
default:
|
|
153
|
+
console.log(`Unhandled event type: ${event.type}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
103
157
|
|
|
104
|
-
|
|
105
|
-
const handler = eventHandlers[event.type];
|
|
158
|
+
### Step 4: Idempotency — Prevent Duplicate Processing
|
|
106
159
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
160
|
+
```typescript
|
|
161
|
+
// lib/idempotency.ts
|
|
162
|
+
// Vercel may retry webhook delivery — track processed event IDs
|
|
163
|
+
const processedEvents = new Set<string>(); // Use Redis in production
|
|
164
|
+
|
|
165
|
+
async function processWebhookIdempotent(
|
|
166
|
+
event: VercelWebhookEvent,
|
|
167
|
+
handler: (e: VercelWebhookEvent) => Promise<void>
|
|
168
|
+
): Promise<boolean> {
|
|
169
|
+
if (processedEvents.has(event.id)) {
|
|
170
|
+
console.log(`Skipping duplicate event: ${event.id}`);
|
|
171
|
+
return false;
|
|
110
172
|
}
|
|
111
173
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error(`Failed to process ${event.type}: ${event.id}`, error);
|
|
117
|
-
throw error; // Rethrow to trigger retry
|
|
118
|
-
}
|
|
174
|
+
await handler(event);
|
|
175
|
+
processedEvents.add(event.id);
|
|
176
|
+
return true;
|
|
119
177
|
}
|
|
120
178
|
```
|
|
121
179
|
|
|
122
|
-
|
|
180
|
+
### Step 5: Slack Notification Example
|
|
123
181
|
|
|
124
182
|
```typescript
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
183
|
+
// lib/notifications.ts
|
|
184
|
+
async function notifyProductionDeploy(event: VercelWebhookEvent): Promise<void> {
|
|
185
|
+
const { deployment, project, user } = event.payload;
|
|
186
|
+
|
|
187
|
+
await fetch(process.env.SLACK_WEBHOOK_URL!, {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
headers: { 'Content-Type': 'application/json' },
|
|
190
|
+
body: JSON.stringify({
|
|
191
|
+
text: `Production deploy complete`,
|
|
192
|
+
blocks: [
|
|
193
|
+
{
|
|
194
|
+
type: 'section',
|
|
195
|
+
text: {
|
|
196
|
+
type: 'mrkdwn',
|
|
197
|
+
text: [
|
|
198
|
+
`*${project.name}* deployed to production`,
|
|
199
|
+
`By: ${user.username}`,
|
|
200
|
+
`URL: https://${deployment.url}`,
|
|
201
|
+
`Commit: ${deployment.meta?.githubCommitMessage ?? 'N/A'}`,
|
|
202
|
+
].join('\n'),
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
133
208
|
}
|
|
134
209
|
|
|
135
|
-
async function
|
|
136
|
-
|
|
137
|
-
|
|
210
|
+
async function notifyDeploymentError(event: VercelWebhookEvent): Promise<void> {
|
|
211
|
+
await fetch(process.env.SLACK_WEBHOOK_URL!, {
|
|
212
|
+
method: 'POST',
|
|
213
|
+
headers: { 'Content-Type': 'application/json' },
|
|
214
|
+
body: JSON.stringify({
|
|
215
|
+
text: `Deployment FAILED for ${event.payload.project.name} — ${event.payload.deployment.id}`,
|
|
216
|
+
}),
|
|
217
|
+
});
|
|
138
218
|
}
|
|
139
219
|
```
|
|
140
220
|
|
|
141
|
-
|
|
221
|
+
### Step 6: Test Webhooks Locally
|
|
142
222
|
|
|
143
223
|
```bash
|
|
144
|
-
# Use Vercel CLI to
|
|
145
|
-
|
|
224
|
+
# Use the Vercel CLI to test webhook signatures
|
|
225
|
+
# Or use a tunnel service for local testing
|
|
226
|
+
npx localtunnel --port 3000
|
|
227
|
+
# Gives you a public URL like https://xxx.loca.lt
|
|
146
228
|
|
|
147
|
-
#
|
|
148
|
-
curl -X POST
|
|
229
|
+
# Send a test webhook payload
|
|
230
|
+
curl -X POST http://localhost:3000/api/webhooks/vercel \
|
|
149
231
|
-H "Content-Type: application/json" \
|
|
150
|
-
-
|
|
232
|
+
-H "x-vercel-signature: $(echo -n '{"type":"deployment.ready","id":"test"}' | openssl dgst -sha1 -hmac 'your-secret' | awk '{print $2}')" \
|
|
233
|
+
-d '{"type":"deployment.ready","id":"test"}'
|
|
151
234
|
```
|
|
152
235
|
|
|
153
|
-
##
|
|
154
|
-
|
|
155
|
-
### Step 1: Register Webhook Endpoint
|
|
156
|
-
Configure your webhook URL in the Vercel dashboard.
|
|
236
|
+
## Webhook Event Types
|
|
157
237
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
238
|
+
| Event | Trigger |
|
|
239
|
+
|-------|---------|
|
|
240
|
+
| `deployment.created` | New deployment started building |
|
|
241
|
+
| `deployment.ready` | Deployment build completed successfully |
|
|
242
|
+
| `deployment.error` | Deployment build failed |
|
|
243
|
+
| `deployment.canceled` | Deployment was canceled |
|
|
244
|
+
| `project.created` | New project created |
|
|
245
|
+
| `project.removed` | Project deleted |
|
|
246
|
+
| `domain.created` | Domain added to project |
|
|
247
|
+
| `integration.configuration.removed` | Integration uninstalled |
|
|
166
248
|
|
|
167
249
|
## Output
|
|
168
|
-
- Secure webhook endpoint
|
|
169
|
-
- Signature validation enabled
|
|
170
|
-
- Event handlers implemented
|
|
171
|
-
- Replay attack protection active
|
|
172
|
-
|
|
173
|
-
## Error Handling
|
|
174
|
-
| Issue | Cause | Solution |
|
|
175
|
-
|-------|-------|----------|
|
|
176
|
-
| Invalid signature | Wrong secret | Verify webhook secret |
|
|
177
|
-
| Timestamp rejected | Clock drift | Check server time sync |
|
|
178
|
-
| Duplicate events | Missing idempotency | Implement event ID tracking |
|
|
179
|
-
| Handler timeout | Slow processing | Use async queue |
|
|
180
250
|
|
|
181
|
-
|
|
251
|
+
- Webhook endpoint with HMAC signature verification
|
|
252
|
+
- Event handlers for deployment lifecycle events
|
|
253
|
+
- Idempotent processing preventing duplicates
|
|
254
|
+
- Slack notifications for production deploys and failures
|
|
182
255
|
|
|
183
|
-
|
|
184
|
-
```bash
|
|
185
|
-
# Use ngrok to expose local server
|
|
186
|
-
ngrok http 3000
|
|
256
|
+
## Error Handling
|
|
187
257
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
258
|
+
| Error | Cause | Solution |
|
|
259
|
+
|-------|-------|----------|
|
|
260
|
+
| `401 Invalid signature` | Wrong webhook secret or body mismatch | Verify secret matches dashboard, use raw body for HMAC |
|
|
261
|
+
| Webhook not received | Endpoint not publicly accessible | Ensure HTTPS, check firewall rules |
|
|
262
|
+
| Duplicate processing | Webhook retried by Vercel | Implement idempotency with event ID tracking |
|
|
263
|
+
| `504 timeout` on webhook endpoint | Handler takes too long | Return 200 immediately, process async in background |
|
|
264
|
+
| Missing `x-vercel-signature` | Not a real Vercel webhook | Reject requests without the signature header |
|
|
193
265
|
|
|
194
266
|
## Resources
|
|
195
|
-
|
|
196
|
-
- [
|
|
267
|
+
|
|
268
|
+
- [Vercel Webhooks API](https://vercel.com/docs/webhooks/webhooks-api)
|
|
269
|
+
- [Setting Up Webhooks](https://vercel.com/docs/webhooks)
|
|
270
|
+
- [Deploy Hooks](https://vercel.com/docs/deploy-hooks)
|
|
271
|
+
- [Integration Webhooks](https://vercel.com/docs/integrations/create-integration)
|
|
197
272
|
|
|
198
273
|
## Next Steps
|
|
199
|
-
|
|
274
|
+
|
|
275
|
+
For performance optimization, see `vercel-performance-tuning`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Error Handling Reference
|
|
2
|
+
|
|
3
|
+
| Issue | Cause | Solution |
|
|
4
|
+
|-------|-------|----------|
|
|
5
|
+
| Invalid signature | Wrong secret | Verify webhook secret |
|
|
6
|
+
| Timestamp rejected | Clock drift | Check server time sync |
|
|
7
|
+
| Duplicate events | Missing idempotency | Implement event ID tracking |
|
|
8
|
+
| Handler timeout | Slow processing | Use async queue |
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Event Handler Pattern
|
|
2
|
+
|
|
3
|
+
## Event Handler Pattern
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
type VercelEventType = 'resource.created' | 'resource.updated' | 'resource.deleted';
|
|
7
|
+
|
|
8
|
+
interface VercelEvent {
|
|
9
|
+
id: string;
|
|
10
|
+
type: VercelEventType;
|
|
11
|
+
data: Record<string, any>;
|
|
12
|
+
created: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const eventHandlers: Record<VercelEventType, (data: any) => Promise<void>> = {
|
|
16
|
+
'resource.created': async (data) => { /* handle */ },
|
|
17
|
+
'resource.updated': async (data) => { /* handle */ },
|
|
18
|
+
'resource.deleted': async (data) => { /* handle */ }
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
async function handleVercelEvent(event: VercelEvent): Promise<void> {
|
|
22
|
+
const handler = eventHandlers[event.type];
|
|
23
|
+
|
|
24
|
+
if (!handler) {
|
|
25
|
+
console.log(`Unhandled event type: ${event.type}`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await handler(event.data);
|
|
31
|
+
console.log(`Processed ${event.type}: ${event.id}`);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(`Failed to process ${event.type}: ${event.id}`, error);
|
|
34
|
+
throw error; // Rethrow to trigger retry
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## Examples
|
|
2
|
+
|
|
3
|
+
### Testing Webhooks Locally
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Use ngrok to expose local server
|
|
7
|
+
ngrok http 3000
|
|
8
|
+
|
|
9
|
+
# Send test webhook
|
|
10
|
+
curl -X POST https://your-ngrok-url/webhooks/vercel \
|
|
11
|
+
-H "Content-Type: application/json" \
|
|
12
|
+
-d '{"type": "test", "data": {}}'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
*[Tons of Skills](https://tonsofskills.com) by [Intent Solutions](https://intentsolutions.io) | [jeremylongshore.com](https://jeremylongshore.com)*
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Signature Verification
|
|
2
|
+
|
|
3
|
+
## Signature Verification
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
function verifyVercelSignature(
|
|
7
|
+
payload: Buffer,
|
|
8
|
+
signature: string,
|
|
9
|
+
timestamp: string
|
|
10
|
+
): boolean {
|
|
11
|
+
const secret = process.env.VERCEL_WEBHOOK_SECRET!;
|
|
12
|
+
|
|
13
|
+
// Reject old timestamps (replay attack protection)
|
|
14
|
+
const timestampAge = Date.now() - parseInt(timestamp) * 1000;
|
|
15
|
+
if (timestampAge > 300000) { // 5 minutes
|
|
16
|
+
console.error('Webhook timestamp too old');
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Compute expected signature
|
|
21
|
+
const signedPayload = `${timestamp}.${payload.toString()}`;
|
|
22
|
+
const expectedSignature = crypto
|
|
23
|
+
.createHmac('sha256', secret)
|
|
24
|
+
.update(signedPayload)
|
|
25
|
+
.digest('hex');
|
|
26
|
+
|
|
27
|
+
// Timing-safe comparison
|
|
28
|
+
return crypto.timingSafeEqual(
|
|
29
|
+
Buffer.from(signature),
|
|
30
|
+
Buffer.from(expectedSignature)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|