Webhooks Reference
ClinicFlow processes real-time external events using secure HTTP webhook endpoints. Incoming webhooks are verified via cryptographic signatures before payload execution.
1. WhatsApp Meta Cloud API Webhook
Section titled “1. WhatsApp Meta Cloud API Webhook”Endpoint Specifications
Section titled “Endpoint Specifications”- Path:
POST /webhooks/whatsapp - Verification Path:
GET /webhooks/whatsapp - Public URL:
https://api.clinicflow.lat/webhooks/whatsapp - Authentication: HMAC-SHA256 Payload Signature (
X-Hub-Signature-256)
Webhook Verification (GET)
Section titled “Webhook Verification (GET)”When configuring the webhook in the Meta Developer Console, Meta sends a GET request with challenge query parameters.
Query Parameters:
hub.mode: Set tosubscribe.hub.verify_token: Must matchWHATSAPP_WEBHOOK_VERIFY_TOKEN.hub.challenge: Random challenge string.
Success Response: Returns hub.challenge as plain text with HTTP status 200 OK.
Inbound Message Notification (POST)
Section titled “Inbound Message Notification (POST)”Sent by Meta whenever a patient messages a clinic’s WhatsApp number.
Security & Signature Verification
Section titled “Security & Signature Verification”Every POST request contains an X-Hub-Signature-256 header formatted as sha256=<signature>.
- The backend computes the HMAC-SHA256 hash of the raw HTTP request body using
WHATSAPP_APP_SECRET. - The signature is compared using
crypto.timingSafeEqualto prevent timing attacks. - Requests failing signature comparison are rejected with
401 Unauthorized.
Deduplication & Race Condition Protection
Section titled “Deduplication & Race Condition Protection”- 24-Hour Event Deduplication: Inbound message IDs (
wamid) are cached in Redis (dedup:wamid:<id>) for 24 hours. Duplicate deliveries are acknowledged (200 OK) and ignored. - Per-Phone Concurrency Lock: Distributed Redis locks (
lock:phone:<phone_number>) ensure sequential message handling when a patient sends multiple rapid messages.
Sample Inbound Payload
Section titled “Sample Inbound Payload”{ "object": "whatsapp_business_account", "entry": [ { "id": "100234567890123", "changes": [ { "value": "messages", "field": "messages", "value": { "messaging_product": "whatsapp", "metadata": { "display_phone_number": "+50370001122", "phone_number_id": "109876543210987" }, "contacts": [ { "profile": { "name": "Carlos Gomez" }, "wa_id": "50370001122" } ], "messages": [ { "from": "50370001122", "id": "wamid.HBgMNTAzNzAwMDExMjIVAgARGBI1RkUzNDVDN0U4OTAxM0EzAA==", "timestamp": "1773800000", "text": { "body": "Hola, quisiera agendar una cita de limpieza" }, "type": "text" } ] } } ] } ]}2. LemonSqueezy Billing Webhook
Section titled “2. LemonSqueezy Billing Webhook”Endpoint Specifications
Section titled “Endpoint Specifications”- Path:
POST /webhooks/lemonsqueezy - Public URL:
https://api.clinicflow.lat/webhooks/lemonsqueezy - Authentication: HMAC-SHA256 Signature (
X-Signature)
Signature Verification
Section titled “Signature Verification”LemonSqueezy signs every webhook payload with the LEMONSQUEEZY_SIGNING_SECRET. The backend computes the HMAC-SHA256 signature over the raw request body and verifies it using crypto.timingSafeEqual.
Supported Event Types
Section titled “Supported Event Types”order_created
Section titled “order_created”Fired when a clinic completes a SaaS subscription purchase or upgrade.
{ "meta": { "event_name": "order_created", "custom_data": { "clinic_id": "8f3b2c1a-4e5f-6a7b-8c9d-0e1f2a3b4c5d" } }, "data": { "type": "orders", "id": "123456", "attributes": { "order_number": 1001, "user_email": "admin@clinicasanfrancisco.com", "status": "paid", "total": 4900, "first_order_item": { "variant_id": 98765, "variant_name": "Pro Tier Monthly" } } }}Actions Executed:
- Validates
custom_data.clinic_id. - Updates
clinics.subscription_plan(basic,pro,clinica). - Updates
clinics.subscription_expires_atto the upcoming billing cycle end date. - Logs subscription order details in
ls_orders.
subscription_updated
Section titled “subscription_updated”Fired on recurring subscription renewals or plan cancellations.
Actions Executed:
- Active / Renewed: Extends
subscription_expires_at. - Cancelled / Past Due: Sets subscription status to grace period or triggers feature lock (
downgradeReconciliationJob.ts).
3. Webhook Operational Best Practices
Section titled “3. Webhook Operational Best Practices”- Fast Response Acknowledgment: Webhook endpoints return
200 OKwithin 500ms before triggering long-running processing tasks. Heavy workloads are delegated to BullMQ workers. - Idempotency Guarantee: All handlers check event uniqueness using database unique constraints (
ls_orders.order_id) or Redis keys before mutating database state.