> ## Documentation Index
> Fetch the complete documentation index at: https://flatfile.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Examples

> Standardized webhook patterns for Flatfile integrations

This file contains standardized webhook examples that can be referenced across all guides to ensure consistency.

## Basic Webhook Setup

### Environment Configuration

```bash theme={null}
# Primary webhook endpoint
WEBHOOK_URL=https://your-app.com/webhook/flatfile

# Backup/testing endpoint (optional)
WEBHOOK_TEST_URL=https://webhook.site/your-unique-id
```

## Webhook Request Patterns

### Basic POST Request with Fetch

```javascript theme={null}
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}`);
}
```

### POST Request with Authentication

```javascript theme={null}
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)
});
```

### Using Axios (Alternative)

```javascript theme={null}
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
});
```

## Complete Webhook Integration Patterns

### Workbook Action with Webhook

```javascript theme={null}
// 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."
        }
      });
    }
  }
);
```

### Record Hook with Webhook

```javascript theme={null}
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;
  })
);
```

## Webhook Testing Patterns

### Using webhook.site for Testing

```javascript theme={null}
// 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;
```

### Local Development Testing

```javascript theme={null}
// Use ngrok or similar for local testing
const webhookUrl = process.env.WEBHOOK_URL || 
  "https://your-ngrok-url.ngrok.io/webhook/flatfile";
```

## Error Handling Best Practices

### Comprehensive Error Handling

```javascript theme={null}
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));
    }
  }
}
```

## Security Considerations

1. **Always use HTTPS** for webhook URLs
2. **Validate webhook signatures** when possible
3. **Use authentication tokens** for sensitive endpoints
4. **Implement proper error handling** to avoid exposing sensitive information
5. **Set reasonable timeouts** to prevent hanging requests
6. **Use environment variables** for URLs and credentials
