Hen Hideaways
APIZapier API

Webhooks

Set up real-time webhook notifications for new enquiries.

Webhooks

Webhooks allow you to receive real-time HTTP notifications when events occur in your Hen Hideaways account. Instead of repeatedly polling the API to check for new enquiries, webhooks push updates to your server or integration platform instantly.

Overview

When you subscribe to a webhook, Hen Hideaways will send an HTTP POST request to your specified URL whenever the subscribed event occurs.

Benefits:

  • Real-time - Notifications arrive within seconds of the event
  • Efficient - No need to poll the API repeatedly
  • Scalable - Handle hundreds of enquiries without additional API calls
  • Reliable - Automatic retries if delivery fails

Supported Events

Currently supported webhook events:

EventTrigger
enquiry.createdA new guest enquiry is submitted for one of your properties

More events will be added in the future (e.g., enquiry.updated, booking.confirmed).

Creating a Webhook Subscription

Zapier handles webhook subscriptions automatically when you set up a "New Enquiry (Instant)" trigger. No manual setup required!

Via API

You can also create webhook subscriptions programmatically:

Endpoint: POST /api/zapier/v1/hooks/enquiries

Request:

curl -X POST "https://www.henhideaways.com/api/zapier/v1/hooks/enquiries" \
  -H "Authorization: Bearer hh_owner_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "targetUrl": "https://your-server.com/webhooks/enquiries",
    "propertyId": null
  }'

Parameters:

FieldTypeRequiredDescription
targetUrlstring (URL)YesYour webhook endpoint that will receive the notifications
propertyIdstring (UUID)NoFilter to specific property. Use null for all properties

Response:

{
  "id": "subscription-uuid",
  "targetUrl": "https://your-server.com/webhooks/enquiries",
  "propertyId": null,
  "event": "enquiry.created"
}

Webhook Payload

When an enquiry.created event occurs, Hen Hideaways sends a POST request to your target URL with the following payload:

{
  "event": "enquiry.created",
  "enquiry_id": "550e8400-e29b-41d4-a716-446655440000",
  "property_id": "660e8400-e29b-41d4-a716-446655440111",
  "property_title": "Luxury Hen House in Brighton",
  "property_url": "https://www.henhideaways.com/properties/luxury-hen-house-brighton",
  "owner_email": "owner@example.com",
  "subject": "Weekend Hen Party Booking Enquiry",
  "message": "Hi, I'm interested in booking your property for a hen weekend in June. We're a group of 12 friends...",
  "status": "sent",
  "sent_at": "2026-04-15T14:30:00Z",
  "user_name": "Sarah Johnson",
  "user_email": "sarah.j@example.com",
  "party_details": {
    "group_size": 12,
    "check_in": "2026-06-15",
    "check_out": "2026-06-17",
    "occasion": "hen_party"
  }
}

Payload Fields:

FieldTypeDescription
eventstringEvent type (enquiry.created)
enquiry_idstring (UUID)Unique identifier for the enquiry
property_idstring (UUID)Property that received the enquiry
property_titlestringName of the property
property_urlstringDirect link to the property page
owner_emailstringEmail of the property owner
subjectstringEnquiry subject line
messagestringFull enquiry message from the guest
statusstringEnquiry status (sent, responded, archived)
sent_atstring (ISO 8601)When the enquiry was submitted
user_namestringGuest's name
user_emailstringGuest's email address
party_detailsobjectBooking details (group size, dates, occasion)

Handling Webhook Deliveries

Responding to Webhooks

Your webhook endpoint must return a 2xx status code (e.g., 200 OK) within 10 seconds to acknowledge successful receipt.

Example Node.js/Express:

app.post('/webhooks/enquiries', (req, res) => {
  const payload = req.body;
  
  console.log('New enquiry received:', payload.enquiry_id);
  console.log('Property:', payload.property_title);
  console.log('Guest:', payload.user_name, payload.user_email);
  
  // Process the webhook asynchronously
  processEnquiry(payload).catch(console.error);
  
  // Respond immediately
  res.status(200).json({ received: true });
});

Example Python/Flask:

@app.route('/webhooks/enquiries', methods=['POST'])
def handle_enquiry_webhook():
    payload = request.json
    
    print(f"New enquiry: {payload['enquiry_id']}")
    print(f"Property: {payload['property_title']}")
    print(f"Guest: {payload['user_name']} ({payload['user_email']})")
    
    # Process asynchronously
    process_enquiry.delay(payload)
    
    # Respond immediately
    return jsonify({'received': True}), 200

Verifying Webhook Authenticity

To verify a webhook came from Hen Hideaways:

  1. Check the request originates from a Hen Hideaways IP address
  2. Validate the enquiry_id exists by fetching it from the API
  3. Implement a secret token in your webhook URL (e.g., https://your-server.com/webhooks/enquiries?secret=your-secret-token)

Example verification:

app.post('/webhooks/enquiries', async (req, res) => {
  const secret = req.query.secret;
  
  // Verify secret token
  if (secret !== process.env.WEBHOOK_SECRET) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  
  const payload = req.body;
  
  // Optionally verify the enquiry exists
  const enquiry = await fetchEnquiryFromAPI(payload.enquiry_id);
  if (!enquiry) {
    return res.status(404).json({ error: 'Enquiry not found' });
  }
  
  // Process webhook
  await processEnquiry(payload);
  
  res.status(200).json({ received: true });
});

Retry Logic

If your endpoint doesn't respond with a 2xx status code, Hen Hideaways will automatically retry:

  • 1st retry: After 1 minute
  • 2nd retry: After 5 minutes
  • 3rd retry: After 15 minutes
  • 4th retry: After 1 hour
  • 5th retry: After 4 hours

After 5 failed attempts, the webhook delivery is abandoned. The subscription remains active for future events.

Monitoring Webhooks

View webhook activity in your Owner Portal:

  1. Go to Settings > Notifications
  2. Scroll to Connected Zapier Hooks
  3. View status, last delivery, and any errors

Status indicators:

  • Active - Subscription is working correctly
  • Failed - Recent deliveries failed (check your endpoint)
  • Inactive - Subscription was manually paused or deleted

Deleting a Webhook Subscription

Via Zapier

Simply turn off or delete the Zap. Zapier will automatically unsubscribe the webhook.

Via API

Endpoint: DELETE /api/zapier/v1/hooks/enquiries

Request:

curl -X DELETE "https://www.henhideaways.com/api/zapier/v1/hooks/enquiries" \
  -H "Authorization: Bearer hh_owner_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "targetUrl": "https://your-server.com/webhooks/enquiries",
    "propertyId": null
  }'

Response:

{
  "success": true
}

Testing Webhooks

Using Zapier

  1. Set up a Zap with "New Enquiry (Instant)" trigger
  2. Add a simple action (e.g., send email to yourself)
  3. Submit a test enquiry via your property page
  4. Check if the action fired

Using webhook.site

For testing without Zapier:

  1. Visit webhook.site
  2. Copy the unique URL
  3. Create a webhook subscription with that URL
  4. Submit a test enquiry
  5. View the webhook payload on webhook.site

Manual Testing

Create a subscription and trigger a webhook:

# 1. Create subscription
curl -X POST "https://www.henhideaways.com/api/zapier/v1/hooks/enquiries" \
  -H "Authorization: Bearer hh_owner_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "targetUrl": "https://webhook.site/your-unique-id",
    "propertyId": null
  }'

# 2. Submit an enquiry via your property page

# 3. Check webhook.site to see the payload

Best Practices

  1. Respond quickly - Return a 200 OK within 10 seconds
  2. Process asynchronously - Don't block the webhook response with slow operations
  3. Implement idempotency - Handle duplicate deliveries gracefully (use enquiry_id to deduplicate)
  4. Log everything - Keep detailed logs for debugging
  5. Monitor failures - Set up alerts for webhook delivery failures
  6. Use HTTPS - Always use secure webhook URLs
  7. Validate payloads - Check the data before processing
  8. Handle errors gracefully - Implement proper error handling

Common Issues

Webhook not firing

  • Verify the subscription is active in Owner Portal
  • Check your endpoint is publicly accessible (not localhost)
  • Ensure HTTPS is properly configured
  • Verify firewall isn't blocking requests

Endpoint receiving duplicate webhooks

  • This is normal - implement idempotency using enquiry_id
  • Store processed IDs in a database/cache
  • Skip already-processed enquiries

Webhooks timing out

  • Your endpoint must respond within 10 seconds
  • Move slow operations (database writes, external API calls) to background jobs
  • Return 200 OK immediately, then process asynchronously

Security Considerations

  1. Use HTTPS - Never use plain HTTP for webhook URLs
  2. Implement authentication - Use a secret token in the URL or verify request signatures
  3. Validate inputs - Don't trust webhook data blindly
  4. Rate limiting - Implement rate limiting to prevent abuse
  5. IP whitelisting - Optionally restrict to Hen Hideaways IP addresses

Need Help?


Example: Complete Webhook Handler

Here's a production-ready webhook handler example:

const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

// In-memory set to track processed enquiry IDs (use Redis in production)
const processedEnquiries = new Set();

app.post('/webhooks/enquiries', async (req, res) => {
  try {
    // 1. Verify secret token
    const secret = req.query.secret;
    if (secret !== process.env.WEBHOOK_SECRET) {
      return res.status(401).json({ error: 'Unauthorized' });
    }

    // 2. Parse payload
    const payload = req.body;
    const { enquiry_id, property_title, user_name, user_email } = payload;

    // 3. Check for duplicates (idempotency)
    if (processedEnquiries.has(enquiry_id)) {
      console.log(`Duplicate webhook for enquiry ${enquiry_id}, skipping`);
      return res.status(200).json({ received: true, duplicate: true });
    }

    // 4. Acknowledge receipt immediately
    res.status(200).json({ received: true });

    // 5. Mark as processed
    processedEnquiries.add(enquiry_id);

    // 6. Process asynchronously (non-blocking)
    processEnquiryAsync(payload);

  } catch (error) {
    console.error('Webhook error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

async function processEnquiryAsync(payload) {
  try {
    // Send notification email
    await sendEmail({
      to: 'reservations@example.com',
      subject: `New Enquiry - ${payload.property_title}`,
      body: `
        New enquiry from ${payload.user_name} (${payload.user_email})
        
        Property: ${payload.property_title}
        Message: ${payload.message}
        
        View in portal: https://owners.henhideaways.com/enquiries
      `
    });

    // Log to database
    await db.enquiries.create({
      enquiry_id: payload.enquiry_id,
      property_id: payload.property_id,
      user_email: payload.user_email,
      received_at: new Date(),
      payload: JSON.stringify(payload)
    });

    console.log(`Successfully processed enquiry ${payload.enquiry_id}`);
  } catch (error) {
    console.error(`Failed to process enquiry ${payload.enquiry_id}:`, error);
    // Implement retry logic or alert monitoring
  }
}

app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

This example demonstrates:

  • Secret token verification
  • Idempotency handling
  • Immediate response
  • Asynchronous processing
  • Error handling
  • Logging

Adapt this pattern to your specific needs!

On this page