Welcome to the new Flatfile Developer Docs!
Standardized webhook patterns for Flatfile integrations
# Primary webhook endpoint
WEBHOOK_URL=https://your-app.com/webhook/flatfile
# Backup/testing endpoint (optional)
WEBHOOK_TEST_URL=https://webhook.site/your-unique-id
const webhookUrl = process.env.WEBHOOK_URL || "https://your-app.com/webhook/flatfile";
const response = await fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": "Flatfile-Integration/1.0"
},
body: JSON.stringify({
event: "data_ready",
workbook: workbookData,
timestamp: new Date().toISOString()
})
});
if (!response.ok) {
throw new Error(`Webhook failed: ${response.status} ${response.statusText}`);
}
const webhookUrl = process.env.WEBHOOK_URL;
const apiKey = await event.secrets("WEBHOOK_API_KEY");
const response = await fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
"X-Flatfile-Event": event.topic
},
body: JSON.stringify(payload)
});
import axios from "axios";
const webhookUrl = process.env.WEBHOOK_URL;
const response = await axios.post(webhookUrl, {
workbook,
sheets,
metadata: {
source: "flatfile",
timestamp: new Date().toISOString()
}
}, {
headers: {
"Content-Type": "application/json"
},
timeout: 30000 // 30 second timeout
});
// Blueprint configuration
actions: [
{
operation: "submitToWebhook",
mode: "foreground",
label: "Submit to External System",
description: "Send data to your external system",
primary: true
}
]
// Listener implementation
listener.on(
"job:ready",
{ job: "workbook:submitToWebhook" },
async (event) => {
const { jobId, workbookId } = event.context;
try {
await api.jobs.ack(jobId, {
info: "Preparing data for submission...",
progress: 10
});
// Collect workbook data
const { data: workbook } = await api.workbooks.get(workbookId);
const { data: sheets } = await api.sheets.list({ workbookId });
const payload = {
workbook: {
...workbook,
sheets: await Promise.all(
sheets.map(async (sheet) => {
const { data: records } = await api.records.get(sheet.id);
return { ...sheet, records: records.records };
})
)
}
};
// Send to webhook
const webhookUrl = await event.secrets("WEBHOOK_URL");
const response = await fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});
if (response.ok) {
await api.jobs.complete(jobId, {
outcome: {
message: `Data successfully submitted to ${webhookUrl}`
}
});
} else {
throw new Error(`Webhook returned ${response.status}`);
}
} catch (error) {
await api.jobs.fail(jobId, {
outcome: {
message: "Failed to submit data. Please check your webhook URL and try again."
}
});
}
}
);
import { recordHook } from "@flatfile/plugin-record-hook";
listener.use(
recordHook("contacts", async (record, event) => {
try {
const webhookUrl = process.env.WEBHOOK_URL ||
await event.secrets("WEBHOOK_URL");
const response = await fetch(webhookUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
action: "record_processed",
record: record.toJSON(),
sheet: event.context.sheetId
})
});
if (response.ok) {
record.addInfo("status", "Successfully sent to external system");
} else {
record.addError("status", "Failed to send to external system");
}
} catch (error) {
record.addError("status", "Webhook communication failed");
}
return record;
})
);
// For testing purposes - use webhook.site
const testWebhookUrl = "https://webhook.site/your-unique-id";
// In production, use your actual endpoint
const webhookUrl = process.env.NODE_ENV === "production"
? process.env.WEBHOOK_URL
: testWebhookUrl;
// Use ngrok or similar for local testing
const webhookUrl = process.env.WEBHOOK_URL ||
"https://your-ngrok-url.ngrok.io/webhook/flatfile";
async function sendWebhook(url, payload, retries = 3) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Attempt": attempt.toString()
},
body: JSON.stringify(payload),
timeout: 30000
});
if (response.ok) {
return await response.json();
}
if (response.status >= 400 && response.status < 500) {
// Client error - don't retry
throw new Error(`Client error: ${response.status}`);
}
// Server error - retry
if (attempt === retries) {
throw new Error(`Server error after ${retries} attempts: ${response.status}`);
}
} catch (error) {
if (attempt === retries) {
throw error;
}
// Wait before retry (exponential backoff)
await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
}
}
}