Webhook Service

Webhook delivery system with event subscriptions, retry logic, and Redis event consumption

Webhook Service (Port 8007)

The webhook-service manages webhook subscriptions, delivers events to external URLs, and handles retry logic.

Database

Database: webhook_db

TablePurpose
webhook_subscriptionsWebhook endpoint configurations
webhook_deliveriesDelivery attempt records

API Endpoints

MethodPathPurpose
GET/webhooksList webhooks
POST/webhooksCreate webhook
DELETE/webhooks/{id}Delete webhook
POST/webhooks/{id}/testTest webhook delivery
GET/webhooks/{id}/deliveriesList delivery history

Event Consumption

The webhook-service subscribes to the Redis webhook:events channel and processes events asynchronously:

@celery_app.task
async def process_webhook_event(event_data: dict):
    webhooks = await webhook_repo.find_by_event_type(
        event_data["event_type"],
        event_data["tenant_id"]
    )
    for webhook in webhooks:
        await deliver_webhook(webhook, event_data)

Retry Logic

Failed deliveries are retried with exponential backoff:

RETRY_SCHEDULE = [60, 300, 900, 3600, 21600]  # 1m, 5m, 15m, 1h, 6h

async def deliver_webhook(webhook, event):
    for attempt, delay in enumerate(RETRY_SCHEDULE):
        try:
            async with httpx.AsyncClient() as client:
                resp = await client.post(webhook.url, json=event, timeout=30)
            if resp.status_code < 500:
                return  # Success
        except Exception:
            pass
        await asyncio.sleep(delay)

The webhook-beat Celery Beat container retries pending webhooks every 1 minute.

Supported Events

The service consumes events from survey-service and data-service:

  • survey.created, survey.updated, survey.deleted, survey.published, survey.unpublished
  • response.submitted, response.deleted, response.invalidated