# Webhook handler examples Here are examples of webhook handlers for different languages and frameworks. ## Best practices When implementing webhook handlers, you'll want to: 1. **Validate the webhook signature immediately** (see Security page) 2. **Return a quick 200 OK response** to acknowledge receipt 3. **Move the actual processing to a background task/job** 4. **Handle different event types** based on the webhook topic 5. **Add error handling and logging** for debugging issues Choose the example that matches your tech stack and adapt it to your needs. ## Python (FastAPI) ```python from fastapi import FastAPI, Request, HTTPException import logging app = FastAPI() logger = logging.getLogger(__name__) async def process_webhook_event(event_data: dict) -> None: """Process the webhook event asynchronously.""" try: topic = event_data.get('topic') match topic: case 'post.created': await handle_post_created(event_data) case 'post.updated': await handle_post_updated(event_data) case _: logger.warning(f"Unhandled webhook topic: {topic}") except Exception as e: logger.error(f"Error processing webhook: {str(e)}", exc_info=True) @app.post("/webhooks/featurebase") async def handle_webhook(request: Request): try: # Return 200 immediately to acknowledge receipt background_tasks = request.background # Get the raw payload payload = await request.json() # Verify webhook signature (implemented in security section) # verify_webhook_signature(request) # Process webhook asynchronously background_tasks.add_task(process_webhook_event, payload) return {"status": "accepted"} except Exception as e: logger.error(f"Webhook handler error: {str(e)}", exc_info=True) raise HTTPException(status_code=500, detail="Internal server error") ``` ## JavaScript (Express) ```javascript const express = require("express"); const router = express.Router(); async function processWebhook(payload) { const { topic, data } = payload; try { switch (topic) { case "post.created": await handlePostCreated(data); break; case "post.updated": await handlePostUpdated(data); break; default: console.warn(`Unhandled webhook topic: ${topic}`); } } catch (error) { console.error("Error processing webhook:", error); } } router.post("/webhooks/featurebase", async (req, res) => { try { // Return 200 immediately to acknowledge receipt res.status(200).json({ status: "accepted" }); // Verify webhook signature (implemented in security section) // await verifyWebhookSignature(req); // Process webhook asynchronously processWebhook(req.body).catch((err) => { console.error("Webhook processing error:", err); }); } catch (error) { console.error("Webhook handler error:", error); } }); module.exports = router; ``` ## PHP (Laravel) ```php json(['status' => 'accepted'], 200)->send(); // Verify webhook signature (implemented in security section) // $this->verifyWebhookSignature($request); // Process webhook asynchronously $this->processWebhook($request->all()); } catch (\Exception $e) { Log::error('Webhook handler error: ' . $e->getMessage()); } } private function processWebhook(array $payload) { try { $topic = $payload['topic'] ?? null; match($topic) { 'post.created' => $this->handlePostCreated($payload), 'post.updated' => $this->handlePostUpdated($payload), default => Log::warning("Unhandled webhook topic: {$topic}") }; } catch (\Exception $e) { Log::error('Error processing webhook: ' . $e->getMessage()); } } } ``` ## Ruby (Rails) ```ruby # app/controllers/webhooks_controller.rb class WebhooksController < ApplicationController skip_before_action :verify_authenticity_token def handle # Return 200 immediately to acknowledge receipt render json: { status: 'accepted' }, status: :ok # Verify webhook signature (implemented in security section) # verify_webhook_signature! # Process asynchronously process_webhook(webhook_params.to_h) rescue StandardError => e Rails.logger.error "Webhook handler error: #{e.message}" end private def process_webhook(payload) topic = payload['topic'] case topic when 'post.created' handle_post_created(payload) when 'post.updated' handle_post_updated(payload) else Rails.logger.warn "Unhandled webhook topic: #{topic}" end rescue StandardError => e Rails.logger.error "Error processing webhook: #{e.message}" end def webhook_params params.permit(:topic, :id, data: {}) end end ``` ## Java (Spring Boot) ```java @RestController @Slf4j public class WebhookController { @PostMapping("/webhooks/featurebase") public ResponseEntity handleWebhook( @RequestBody WebhookPayload payload ) { try { // Return 200 immediately to acknowledge receipt ResponseEntity response = ResponseEntity.ok(new WebhookResponse("accepted")); // Verify webhook signature (implemented in security section) // verifyWebhookSignature(request); // Process asynchronously CompletableFuture.runAsync(() -> processWebhook(payload)); return response; } catch (Exception e) { log.error("Webhook handler error", e); return ResponseEntity .status(HttpStatus.INTERNAL_SERVER_ERROR) .build(); } } private void processWebhook(WebhookPayload payload) { try { switch (payload.getTopic()) { case "post.created": handlePostCreated(payload); break; case "post.updated": handlePostUpdated(payload); break; default: log.warn("Unhandled webhook topic: {}", payload.getTopic()); } } catch (Exception e) { log.error("Error processing webhook", e); } } } ``` ## Go ```go package webhooks import ( "encoding/json" "log" "net/http" ) type WebhookHandler struct { logger *log.Logger } func NewWebhookHandler(logger *log.Logger) *WebhookHandler { return &WebhookHandler{logger: logger} } func (h *WebhookHandler) HandleWebhook(w http.ResponseWriter, r *http.Request) { // Return 200 immediately to acknowledge receipt w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]string{"status": "accepted"}) // Verify webhook signature (implemented in security section) // if err := verifyWebhookSignature(r); err != nil { // h.logger.Printf("Invalid webhook signature: %v", err) // return // } // Parse payload var payload WebhookPayload if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { h.logger.Printf("Error decoding webhook: %v", err) return } // Process asynchronously go h.processWebhook(payload) } func (h *WebhookHandler) processWebhook(payload WebhookPayload) { switch payload.Topic { case "post.created": h.handlePostCreated(payload) case "post.updated": h.handlePostUpdated(payload) default: h.logger.Printf("Unhandled webhook topic: %s", payload.Topic) } } ``` ## C# (.NET) ```csharp [ApiController] [Route("webhooks")] public class WebhookController : ControllerBase { private readonly ILogger _logger; public WebhookController(ILogger logger) { _logger = logger; } [HttpPost("featurebase")] public IActionResult HandleWebhook([FromBody] WebhookPayload payload) { try { // Verify webhook signature (implemented in security section) // await VerifyWebhookSignatureAsync(Request); // Process asynchronously _ = Task.Run(() => ProcessWebhookAsync(payload)); return Ok(new { status = "accepted" }); } catch (Exception ex) { _logger.LogError(ex, "Error handling webhook"); return StatusCode(500); } } private async Task ProcessWebhookAsync(WebhookPayload payload) { try { switch (payload.Topic) { case "post.created": await HandlePostCreatedAsync(payload); break; case "post.updated": await HandlePostUpdatedAsync(payload); break; default: _logger.LogWarning( "Unhandled webhook topic: {Topic}", payload.Topic); break; } } catch (Exception ex) { _logger.LogError(ex, "Error processing webhook"); } } } ```