firecrawl-webhooks-events
Configures secure webhook endpoints and event handling logic to process asynchronous Firecrawl job notifications.
Install
mkdir -p .claude/skills/firecrawl-webhooks-events && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/3531" && unzip -o skill.zip -d .claude/skills/firecrawl-webhooks-events && rm skill.zipInstalls to .claude/skills/firecrawl-webhooks-events
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Implement Firecrawl webhook event handling for async crawl and batchKey capabilities
- →Start Firecrawl crawls with webhook configurations
- →Implement webhook handlers with signature verification
- →Process individual page events as they are scraped
- →Handle crawl completion events
- →Implement polling as a fallback for webhook delivery failures
How it works
The skill enables asynchronous processing of Firecrawl jobs by configuring webhooks to receive real-time notifications. It includes steps for starting crawls with webhook URLs, verifying signatures, and handling different event types.
Inputs & outputs
When to use firecrawl-webhooks-events
- →Implement HMAC-SHA256 signature verification
- →Handle async crawl notifications
- →Build webhook endpoints for batch scrape jobs
- →Process real-time scrape results
About this skill
Firecrawl Webhooks & Events
Overview
Handle Firecrawl webhooks for real-time notifications on async crawl and batch scrape jobs. Instead of polling checkCrawlStatus, configure a webhook URL and Firecrawl will POST events as pages are scraped and jobs complete. Signed with HMAC-SHA256 via X-Firecrawl-Signature.
Webhook Event Types
| Event | Trigger | Payload |
|---|---|---|
crawl.started | Crawl job begins | Job ID, config |
crawl.page | Individual page scraped | Page markdown, metadata |
crawl.completed | Full crawl finishes | All pages array |
crawl.failed | Crawl job errors | Error message |
batch_scrape.completed | Batch scrape finishes | All scraped pages |
Instructions
Step 1: Start Crawl with Webhook
import FirecrawlApp from "@mendable/firecrawl-js";
const firecrawl = new FirecrawlApp({
apiKey: process.env.FIRECRAWL_API_KEY!,
});
// Webhook as string (simple)
const job = await firecrawl.asyncCrawlUrl("https://docs.example.com", {
limit: 100,
scrapeOptions: { formats: ["markdown"] },
webhook: "https://api.yourapp.com/webhooks/firecrawl",
});
console.log(`Crawl started: ${job.id}`);
// Webhook as object (with metadata and event filtering)
const job2 = await firecrawl.asyncCrawlUrl("https://docs.example.com", {
limit: 100,
scrapeOptions: { formats: ["markdown"] },
webhook: {
url: "https://api.yourapp.com/webhooks/firecrawl",
events: ["completed", "page"], // only these events
metadata: {
projectId: "my-project",
triggeredBy: "cron",
},
},
});
Step 2: Webhook Handler with Signature Verification
import express from "express";
import crypto from "crypto";
const app = express();
app.use(express.json());
function verifySignature(body: string, signature: string): boolean {
if (!process.env.FIRECRAWL_WEBHOOK_SECRET) return true; // skip if not configured
const expected = crypto
.createHmac("sha256", process.env.FIRECRAWL_WEBHOOK_SECRET)
.update(body)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}
app.post("/webhooks/firecrawl", express.raw({ type: "application/json" }), async (req, res) => {
const rawBody = req.body.toString();
const signature = req.headers["x-firecrawl-signature"] as string;
if (!verifySignature(rawBody, signature)) {
return res.status(401).json({ error: "Invalid signature" });
}
const { type, id, data, metadata } = JSON.parse(rawBody);
// Respond immediately — process asynchronously
res.status(200).json({ received: true });
switch (type) {
case "crawl.started":
console.log(`Crawl ${id} started`);
break;
case "crawl.page":
await handlePageScraped(id, data, metadata);
break;
case "crawl.completed":
await handleCrawlComplete(id, data, metadata);
break;
case "crawl.failed":
await handleCrawlFailed(id, data);
break;
}
});
Step 3: Process Page Events (Streaming)
async function handlePageScraped(jobId: string, data: any[], metadata: any) {
for (const page of data) {
const doc = {
url: page.metadata?.sourceURL,
title: page.metadata?.title,
markdown: page.markdown,
statusCode: page.metadata?.statusCode,
crawlJobId: jobId,
projectId: metadata?.projectId,
indexedAt: new Date(),
};
// Index page immediately — don't wait for full crawl
await documentStore.upsert(doc);
console.log(`Indexed: ${doc.url} (${doc.markdown?.length || 0} chars)`);
}
}
Step 4: Handle Crawl Completion
async function handleCrawlComplete(jobId: string, data: any[], metadata: any) {
console.log(`Crawl ${jobId} complete: ${data.length} pages`);
// Build search index from all crawled pages
const documents = data
.filter(page => page.markdown && page.markdown.length > 100)
.map(page => ({
id: page.metadata?.sourceURL,
title: page.metadata?.title || "",
content: page.markdown,
url: page.metadata?.sourceURL,
}));
await searchIndex.indexBatch(documents);
console.log(`Indexed ${documents.length} documents for project ${metadata?.projectId}`);
}
async function handleCrawlFailed(jobId: string, data: any) {
console.error(`Crawl ${jobId} failed:`, data.error);
await alerting.send({
severity: "high",
message: `Firecrawl crawl job ${jobId} failed`,
error: data.error,
partialResults: data.partialResults?.length || 0,
});
}
Step 5: Polling as Webhook Fallback
// Fall back to polling if webhook delivery fails
async function pollWithFallback(jobId: string, timeoutMs = 600000) {
const deadline = Date.now() + timeoutMs;
let interval = 2000;
while (Date.now() < deadline) {
const status = await firecrawl.checkCrawlStatus(jobId);
if (status.status === "completed") {
return status.data;
}
if (status.status === "failed") {
throw new Error(`Crawl failed: ${status.error}`);
}
console.log(`Polling: ${status.completed}/${status.total} pages`);
await new Promise(r => setTimeout(r, interval));
interval = Math.min(interval * 1.5, 30000);
}
throw new Error(`Crawl timed out after ${timeoutMs}ms`);
}
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Webhook not received | URL not publicly accessible | Use ngrok for local dev, verify HTTPS |
| Signature mismatch | Wrong secret or body encoding | Use raw body for HMAC, not parsed JSON |
| Duplicate events | Firecrawl retry on non-2xx | Make handler idempotent (dedup by job ID) |
| Webhook timeout | Processing takes too long | Return 200 immediately, process async |
| Lost events | 3 failed retries | Implement polling fallback |
Examples
Local Development with ngrok
set -euo pipefail
# Start ngrok tunnel for local webhook testing
ngrok http 3000
# Use the ngrok URL as your webhook endpoint
# https://abc123.ngrok.io/webhooks/firecrawl
Resources
Next Steps
For deployment setup, see firecrawl-deploy-integration.
When not to use it
- →When the webhook URL is not publicly accessible
- →When the webhook handler takes too long to process events
- →When the Firecrawl webhook secret is misconfigured
Limitations
- →Webhooks may not be received if the URL is not publicly accessible
- →Signature mismatches can occur due to incorrect secret or body encoding
- →Webhook timeouts can happen if processing takes too long
How it compares
This skill provides a method for real-time event-driven processing of Firecrawl jobs via webhooks, which is more efficient than repeatedly polling for job status updates.
Compared to similar skills
firecrawl-webhooks-events side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| firecrawl-webhooks-events (this skill) | 1 | 27d | Caution | Intermediate |
| telegram-bot-builder | 106 | 6mo | Review | Intermediate |
| n8n-expression-syntax | 6 | 4mo | No flags | Beginner |
| reddit-api | 3 | 4mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
telegram-bot-builder
davila7
Expert in building Telegram bots that solve real problems - from simple automation to complex AI-powered bots. Covers bot architecture, the Telegram Bot API, user experience, monetization strategies, and scaling bots to thousands of users. Use when: telegram bot, bot api, telegram automation, chat bot telegram, tg bot.
n8n-expression-syntax
czlonkowski
Validate n8n expression syntax and fix common errors. Use when writing n8n expressions, using {{}} syntax, accessing $json/$node variables, troubleshooting expression errors, or working with webhook data in workflows.
reddit-api
alinaqi
Reddit API with PRAW (Python) and Snoowrap (Node.js)
mcporter
openclaw
Use the mcporter CLI to list, configure, auth, and call MCP servers/tools directly (HTTP or stdio), including ad-hoc servers, config edits, and CLI/type generation.
calcom-api
calcom
Interact with the Cal.com API v2 to manage scheduling, bookings, event types, availability, and calendars. Use this skill when building integrations that need to create or manage bookings, check availability, configure event types, or sync calendars with Cal.com's scheduling infrastructure.
instantly-webhooks-events
jeremylongshore
Implement Instantly webhook signature validation and event handling. Use when setting up webhook endpoints, implementing signature verification, or handling Instantly event notifications securely. Trigger with phrases like "instantly webhook", "instantly events", "instantly webhook signature", "handle instantly events", "instantly notifications".