Skip to main content

Overview

Webhooks are the primary way external systems send data to GoBlue. This guide covers everything you need to know about integrating webhooks with popular platforms, custom applications, and third-party services.
Before starting, make sure you have a GoBlue form created with “Enable capturing” turned on. If you need help creating a form, see our Setup First Form guide.

Understanding Webhook Integration

The Integration Flow

1

External Event

Something happens in your external system (form submission, new lead, purchase, etc.)
2

Webhook Trigger

The external system sends an HTTP POST request to your GoBlue webhook URL with relevant data
3

Data Processing

GoBlue receives and validates the data, then creates a personalized message using your form template
4

Message Queue

The message is added to your queue and will be sent when you run the iOS Shortcut

Required Components

For successful webhook integration, you need:

GoBlue Form

A configured form with webhook URL and message template

External System

Service capable of sending HTTP POST requests with JSON data

Data Mapping

Field mapping between your external system and GoBlue form

Error Handling

Logic to handle failed webhook requests and retry scenarios

Platform-Specific Integrations

Website Contact Forms

Vanilla HTML/JavaScript

Basic implementation for any website:
<form id="contact-form">
  <input type="text" name="firstName" placeholder="First Name" required>
  <input type="text" name="lastName" placeholder="Last Name" required>
  <input type="tel" name="phoneNumber" placeholder="Phone Number" required>
  <input type="email" name="email" placeholder="Email">
  <select name="serviceType" required>
    <option value="">Select Service</option>
    <option value="web development">Web Development</option>
    <option value="consulting">Consulting</option>
    <option value="design">Design</option>
  </select>
  <textarea name="message" placeholder="Tell us about your project"></textarea>
  <button type="submit">Send Message</button>
</form>

<script>
document.getElementById('contact-form').addEventListener('submit', async function(e) {
  e.preventDefault();
  
  // Collect form data
  const formData = new FormData(this);
  const data = Object.fromEntries(formData);
  
  // Add timestamp
  data.submissionTime = new Date().toISOString();
  
  try {
    const response = await fetch('https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data)
    });
    
    if (response.ok) {
      // Success feedback
      document.getElementById('success-message').style.display = 'block';
      this.reset();
    } else {
      throw new Error('Submission failed');
    }
  } catch (error) {
    // Error handling
    document.getElementById('error-message').style.display = 'block';
    console.error('GoBlue webhook error:', error);
  }
});
</script>

WordPress Integration

For WordPress sites, add this to your theme’s functions.php:
// AJAX handler for GoBlue webhook
add_action('wp_ajax_submit_to_goblue', 'handle_goblue_submission');
add_action('wp_ajax_nopriv_submit_to_goblue', 'handle_goblue_submission');

function handle_goblue_submission() {
    // Verify nonce for security
    if (!wp_verify_nonce($_POST['nonce'], 'goblue_contact_form')) {
        wp_send_json_error('Security check failed');
        return;
    }
    
    // Sanitize input data
    $data = array(
        'firstName' => sanitize_text_field($_POST['firstName']),
        'lastName' => sanitize_text_field($_POST['lastName']),
        'phoneNumber' => sanitize_text_field($_POST['phoneNumber']),
        'email' => sanitize_email($_POST['email']),
        'serviceType' => sanitize_text_field($_POST['serviceType']),
        'message' => sanitize_textarea_field($_POST['message']),
        'source' => 'WordPress Website',
        'submissionTime' => current_time('mysql')
    );
    
    // Send to GoBlue
    $response = wp_remote_post('https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook', array(
        'headers' => array('Content-Type' => 'application/json'),
        'body' => json_encode($data),
        'timeout' => 30
    ));
    
    if (is_wp_error($response)) {
        wp_send_json_error('Failed to send message');
    } else {
        $response_code = wp_remote_retrieve_response_code($response);
        if ($response_code === 200) {
            wp_send_json_success('Message sent successfully');
        } else {
            wp_send_json_error('Server error: ' . $response_code);
        }
    }
}

// Enqueue JavaScript for form handling
function enqueue_goblue_scripts() {
    wp_enqueue_script('goblue-contact', get_template_directory_uri() . '/js/goblue-contact.js', array('jquery'), '1.0', true);
    wp_localize_script('goblue-contact', 'goblue_ajax', array(
        'ajax_url' => admin_url('admin-ajax.php'),
        'nonce' => wp_create_nonce('goblue_contact_form')
    ));
}
add_action('wp_enqueue_scripts', 'enqueue_goblue_scripts');
Corresponding JavaScript file (goblue-contact.js):
jQuery(document).ready(function($) {
    $('#contact-form').on('submit', function(e) {
        e.preventDefault();
        
        const formData = new FormData(this);
        formData.append('action', 'submit_to_goblue');
        formData.append('nonce', goblue_ajax.nonce);
        
        $.ajax({
            url: goblue_ajax.ajax_url,
            type: 'POST',
            data: formData,
            processData: false,
            contentType: false,
            success: function(response) {
                if (response.success) {
                    $('.success-message').show();
                    $('#contact-form')[0].reset();
                } else {
                    $('.error-message').text(response.data).show();
                }
            },
            error: function() {
                $('.error-message').text('Connection error. Please try again.').show();
            }
        });
    });
});

CRM Integrations

HubSpot Integration

Using HubSpot workflows to trigger GoBlue messages:
// HubSpot Custom Code Workflow Action
const axios = require('axios');

exports.main = async (event, callback) => {
  // Extract contact properties
  const contact = event.inputFields;
  
  // Prepare data for GoBlue
  const goBlueData = {
    firstName: contact.firstname,
    lastName: contact.lastname,
    phoneNumber: contact.phone,
    email: contact.email,
    company: contact.company,
    leadScore: contact.hubspotscore,
    lifecycle: contact.lifecyclestage,
    source: contact.hs_analytics_source,
    lastContactDate: new Date().toISOString()
  };
  
  try {
    const response = await axios.post(
      'https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook',
      goBlueData,
      {
        headers: {
          'Content-Type': 'application/json'
        },
        timeout: 10000
      }
    );
    
    console.log('GoBlue webhook successful:', response.status);
    callback({ outputFields: { status: 'success', message: 'Sent to GoBlue' } });
    
  } catch (error) {
    console.error('GoBlue webhook failed:', error.message);
    callback({ outputFields: { status: 'error', message: error.message } });
  }
};

Salesforce Integration

Apex trigger to send data to GoBlue:
trigger LeadToGoBlue on Lead (after insert, after update) {
    List<Lead> qualifiedLeads = new List<Lead>();
    
    for (Lead lead : Trigger.new) {
        // Only send qualified leads with phone numbers
        if (lead.Status == 'Qualified' && lead.Phone != null) {
            qualifiedLeads.add(lead);
        }
    }
    
    if (!qualifiedLeads.isEmpty()) {
        GoBlueWebhookService.sendLeadsToGoBlue(qualifiedLeads);
    }
}
Supporting Apex class:
public class GoBlueWebhookService {
    private static final String GOBLUE_WEBHOOK_URL = 'https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook';
    
    @future(callout=true)
    public static void sendLeadsToGoBlue(List<Lead> leads) {
        for (Lead lead : leads) {
            sendLeadToGoBlue(lead);
        }
    }
    
    private static void sendLeadToGoBlue(Lead lead) {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(GOBLUE_WEBHOOK_URL);
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json');
        request.setTimeout(30000);
        
        // Prepare JSON payload
        Map<String, Object> data = new Map<String, Object>();
        data.put('firstName', lead.FirstName);
        data.put('lastName', lead.LastName);
        data.put('phoneNumber', lead.Phone);
        data.put('email', lead.Email);
        data.put('company', lead.Company);
        data.put('leadSource', lead.LeadSource);
        data.put('status', lead.Status);
        data.put('salesforceId', lead.Id);
        
        request.setBody(JSON.serialize(data));
        
        try {
            HttpResponse response = http.send(request);
            
            if (response.getStatusCode() == 200) {
                System.debug('Successfully sent lead to GoBlue: ' + lead.Id);
            } else {
                System.debug('GoBlue webhook failed: ' + response.getStatusCode() + ' - ' + response.getBody());
            }
        } catch (Exception e) {
            System.debug('GoBlue webhook exception: ' + e.getMessage());
        }
    }
}

E-commerce Integrations

Shopify Webhook Handler

Node.js/Express handler for Shopify webhooks:
const express = require('express');
const axios = require('axios');
const crypto = require('crypto');

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

// Middleware to verify Shopify webhook
function verifyShopifyWebhook(req, res, next) {
    const hmac = req.get('X-Shopify-Hmac-Sha256');
    const body = JSON.stringify(req.body);
    const hash = crypto
        .createHmac('sha256', process.env.SHOPIFY_WEBHOOK_SECRET)
        .update(body, 'utf8')
        .digest('base64');
    
    if (hash === hmac) {
        next();
    } else {
        res.status(401).send('Unauthorized');
    }
}

// Handle new order webhook
app.post('/webhooks/shopify/orders/create', verifyShopifyWebhook, async (req, res) => {
    const order = req.body;
    
    // Extract customer and order information
    const goBlueData = {
        firstName: order.customer.first_name,
        lastName: order.customer.last_name,
        phoneNumber: order.customer.phone,
        email: order.customer.email,
        orderNumber: order.order_number,
        orderTotal: order.total_price,
        currency: order.currency,
        items: order.line_items.map(item => ({
            name: item.title,
            quantity: item.quantity,
            price: item.price
        })),
        shippingAddress: {
            address1: order.shipping_address?.address1,
            city: order.shipping_address?.city,
            province: order.shipping_address?.province,
            country: order.shipping_address?.country
        },
        orderDate: order.created_at,
        shopifyOrderId: order.id
    };
    
    try {
        const response = await axios.post(
            'https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook',
            goBlueData,
            {
                headers: { 'Content-Type': 'application/json' },
                timeout: 15000
            }
        );
        
        console.log('Order sent to GoBlue successfully:', order.order_number);
        res.status(200).json({ success: true });
        
    } catch (error) {
        console.error('Failed to send order to GoBlue:', error.message);
        res.status(500).json({ error: 'Failed to process order' });
    }
});

app.listen(3000, () => {
    console.log('Shopify webhook handler listening on port 3000');
});

WooCommerce Integration

PHP webhook handler for WooCommerce:
// Add to your WordPress theme's functions.php or create a plugin

add_action('woocommerce_order_status_completed', 'send_order_to_goblue');
add_action('woocommerce_order_status_processing', 'send_order_to_goblue');

function send_order_to_goblue($order_id) {
    $order = wc_get_order($order_id);
    
    if (!$order) {
        return;
    }
    
    // Prepare order data
    $items = array();
    foreach ($order->get_items() as $item) {
        $items[] = array(
            'name' => $item->get_name(),
            'quantity' => $item->get_quantity(),
            'total' => $item->get_total()
        );
    }
    
    $data = array(
        'firstName' => $order->get_billing_first_name(),
        'lastName' => $order->get_billing_last_name(),
        'phoneNumber' => $order->get_billing_phone(),
        'email' => $order->get_billing_email(),
        'orderNumber' => $order->get_order_number(),
        'orderTotal' => $order->get_total(),
        'currency' => $order->get_currency(),
        'paymentMethod' => $order->get_payment_method_title(),
        'items' => $items,
        'orderStatus' => $order->get_status(),
        'orderDate' => $order->get_date_created()->format('c'),
        'woocommerceOrderId' => $order_id
    );
    
    // Send to GoBlue
    $response = wp_remote_post('https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook', array(
        'headers' => array('Content-Type' => 'application/json'),
        'body' => json_encode($data),
        'timeout' => 30
    ));
    
    if (is_wp_error($response)) {
        error_log('GoBlue webhook failed for order ' . $order_id . ': ' . $response->get_error_message());
    } else {
        $response_code = wp_remote_retrieve_response_code($response);
        if ($response_code === 200) {
            error_log('Order ' . $order_id . ' sent to GoBlue successfully');
        } else {
            error_log('GoBlue webhook failed for order ' . $order_id . ': HTTP ' . $response_code);
        }
    }
}

Form Builder Integrations

Typeform Integration

Using Typeform webhooks to send responses to GoBlue:
// Webhook handler for Typeform submissions
app.post('/webhooks/typeform', async (req, res) => {
    const submission = req.body;
    
    if (!submission.form_response) {
        return res.status(400).json({ error: 'Invalid Typeform webhook data' });
    }
    
    const formResponse = submission.form_response;
    const answers = formResponse.answers;
    
    // Map Typeform answers to GoBlue fields
    const goBlueData = {
        typeformResponseId: formResponse.token,
        submissionDate: formResponse.submitted_at
    };
    
    // Extract answers based on field IDs or references
    answers.forEach(answer => {
        switch (answer.field.ref) {
            case 'first_name':
                goBlueData.firstName = answer.text;
                break;
            case 'last_name':
                goBlueData.lastName = answer.text;
                break;
            case 'phone_number':
                goBlueData.phoneNumber = answer.phone_number;
                break;
            case 'email':
                goBlueData.email = answer.email;
                break;
            case 'service_type':
                goBlueData.serviceType = answer.choice.label;
                break;
            case 'budget':
                goBlueData.budget = answer.choice.label;
                break;
            case 'message':
                goBlueData.message = answer.text;
                break;
        }
    });
    
    try {
        const response = await axios.post(
            'https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook',
            goBlueData,
            {
                headers: { 'Content-Type': 'application/json' },
                timeout: 10000
            }
        );
        
        console.log('Typeform submission sent to GoBlue');
        res.status(200).json({ success: true });
        
    } catch (error) {
        console.error('Failed to send Typeform submission to GoBlue:', error.message);
        res.status(500).json({ error: 'Webhook processing failed' });
    }
});

Google Forms Integration

Using Google Apps Script to send form responses to GoBlue:
function onFormSubmit(e) {
  const formResponse = e.response;
  const itemResponses = formResponse.getItemResponses();
  
  // Map form responses to GoBlue data structure
  const goBlueData = {
    googleFormTimestamp: formResponse.getTimestamp().toISOString(),
    googleFormId: formResponse.getId()
  };
  
  // Extract responses by question title
  itemResponses.forEach(itemResponse => {
    const title = itemResponse.getItem().getTitle().toLowerCase();
    const response = itemResponse.getResponse();
    
    if (title.includes('first name')) {
      goBlueData.firstName = response;
    } else if (title.includes('last name')) {
      goBlueData.lastName = response;
    } else if (title.includes('phone')) {
      goBlueData.phoneNumber = response;
    } else if (title.includes('email')) {
      goBlueData.email = response;
    } else if (title.includes('service')) {
      goBlueData.serviceType = response;
    } else if (title.includes('message')) {
      goBlueData.message = response;
    }
  });
  
  // Send to GoBlue
  const payload = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    payload: JSON.stringify(goBlueData)
  };
  
  try {
    const response = UrlFetchApp.fetch(
      'https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook',
      payload
    );
    
    if (response.getResponseCode() === 200) {
      console.log('Successfully sent form response to GoBlue');
    } else {
      console.error('GoBlue webhook failed:', response.getResponseCode());
    }
  } catch (error) {
    console.error('Error sending to GoBlue:', error.toString());
  }
}

// Set up the trigger (run this once to install the trigger)
function installTrigger() {
  const form = FormApp.getActiveForm();
  ScriptApp.newTrigger('onFormSubmit')
    .source(form)
    .onFormSubmit()
    .create();
}

Advanced Webhook Patterns

Batch Processing

For high-volume scenarios, implement batch processing:
class GoBlueWebhookBatcher {
    constructor(webhookUrl, batchSize = 10, flushInterval = 5000) {
        this.webhookUrl = webhookUrl;
        this.batchSize = batchSize;
        this.flushInterval = flushInterval;
        this.batch = [];
        this.timer = null;
    }
    
    add(data) {
        this.batch.push(data);
        
        if (this.batch.length >= this.batchSize) {
            this.flush();
        } else if (!this.timer) {
            this.timer = setTimeout(() => this.flush(), this.flushInterval);
        }
    }
    
    async flush() {
        if (this.batch.length === 0) return;
        
        const currentBatch = [...this.batch];
        this.batch = [];
        
        if (this.timer) {
            clearTimeout(this.timer);
            this.timer = null;
        }
        
        // Send batch requests in parallel
        const promises = currentBatch.map(data => 
            this.sendToGoBlue(data).catch(error => {
                console.error('Batch item failed:', error);
                return { error: error.message, data };
            })
        );
        
        const results = await Promise.all(promises);
        
        // Handle any failures
        const failures = results.filter(result => result.error);
        if (failures.length > 0) {
            console.warn(`${failures.length} webhook calls failed`);
            // Implement retry logic here
        }
    }
    
    async sendToGoBlue(data) {
        const response = await fetch(this.webhookUrl, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(data)
        });
        
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
        }
        
        return response.json();
    }
}

// Usage
const batcher = new GoBlueWebhookBatcher('https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook');

// Add items to batch
batcher.add({ firstName: 'John', phoneNumber: '+1234567890' });
batcher.add({ firstName: 'Jane', phoneNumber: '+1987654321' });

Retry Logic with Exponential Backoff

Implement robust error handling:
class GoBlueWebhookClient {
    constructor(webhookUrl, options = {}) {
        this.webhookUrl = webhookUrl;
        this.maxRetries = options.maxRetries || 3;
        this.baseDelay = options.baseDelay || 1000;
        this.maxDelay = options.maxDelay || 30000;
    }
    
    async send(data, attempt = 1) {
        try {
            const response = await fetch(this.webhookUrl, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(data),
                timeout: 15000
            });
            
            if (response.ok) {
                return await response.json();
            }
            
            // Handle specific error codes
            if (response.status === 429) {
                // Rate limited - always retry
                throw new Error('Rate limited');
            } else if (response.status >= 500) {
                // Server error - retry
                throw new Error(`Server error: ${response.status}`);
            } else if (response.status === 400) {
                // Client error - don't retry
                const errorData = await response.text();
                throw new Error(`Client error: ${errorData}`);
            }
            
            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            
        } catch (error) {
            if (attempt <= this.maxRetries && this.shouldRetry(error)) {
                const delay = Math.min(
                    this.baseDelay * Math.pow(2, attempt - 1),
                    this.maxDelay
                );
                
                console.warn(`Attempt ${attempt} failed, retrying in ${delay}ms:`, error.message);
                
                await new Promise(resolve => setTimeout(resolve, delay));
                return this.send(data, attempt + 1);
            }
            
            throw error;
        }
    }
    
    shouldRetry(error) {
        // Retry on network errors and rate limiting
        return error.message.includes('Rate limited') ||
               error.message.includes('Server error') ||
               error.name === 'TypeError' || // Network errors
               error.name === 'TimeoutError';
    }
}

// Usage
const client = new GoBlueWebhookClient('https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook');

try {
    await client.send({
        firstName: 'John',
        lastName: 'Doe',
        phoneNumber: '+1234567890'
    });
    console.log('Webhook sent successfully');
} catch (error) {
    console.error('All retry attempts failed:', error.message);
}

Testing Webhook Integrations

Manual Testing

Use curl or Postman to test your webhooks:
# Test basic webhook
curl -X POST https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Test",
    "lastName": "User",
    "phoneNumber": "+1234567890",
    "email": "[email protected]",
    "source": "Manual Test"
  }'

# Test with missing required field
curl -X POST https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Test",
    "lastName": "User",
    "email": "[email protected]"
  }'

# Test rate limiting
for i in {1..150}; do
  curl -X POST https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook \
    -H "Content-Type: application/json" \
    -d '{"firstName":"Test'$i'","phoneNumber":"+123456'$i'"}' &
done
wait

Automated Testing

Create test suites for your webhook integrations:
// Jest test example
const axios = require('axios');

describe('GoBlue Webhook Integration', () => {
    const webhookUrl = 'https://api.goblue.app/v1/forms/YOUR_FORM_ID/webhook';
    
    test('should accept valid webhook data', async () => {
        const data = {
            firstName: 'John',
            lastName: 'Doe',
            phoneNumber: '+1234567890',
            email: '[email protected]'
        };
        
        const response = await axios.post(webhookUrl, data);
        
        expect(response.status).toBe(200);
        expect(response.data.status).toBe('success');
    });
    
    test('should reject data without phone number', async () => {
        const data = {
            firstName: 'John',
            lastName: 'Doe',
            email: '[email protected]'
        };
        
        try {
            await axios.post(webhookUrl, data);
            fail('Should have thrown an error');
        } catch (error) {
            expect(error.response.status).toBe(400);
        }
    });
    
    test('should handle malformed JSON', async () => {
        try {
            await axios.post(webhookUrl, 'invalid json', {
                headers: { 'Content-Type': 'application/json' }
            });
            fail('Should have thrown an error');
        } catch (error) {
            expect(error.response.status).toBe(400);
        }
    });
});

Monitoring and Debugging

Logging Webhook Activity

Implement comprehensive logging:
const winston = require('winston');

const logger = winston.createLogger({
    level: 'info',
    format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
    ),
    transports: [
        new winston.transports.File({ filename: 'goblue-webhooks.log' }),
        new winston.transports.Console({ format: winston.format.simple() })
    ]
});

async function sendToGoBlue(data) {
    const requestId = Date.now().toString(36);
    
    logger.info('Sending webhook to GoBlue', {
        requestId,
        data: { ...data, phoneNumber: data.phoneNumber ? '[REDACTED]' : undefined }
    });
    
    try {
        const startTime = Date.now();
        
        const response = await axios.post(webhookUrl, data, {
            headers: { 'Content-Type': 'application/json' },
            timeout: 15000
        });
        
        const duration = Date.now() - startTime;
        
        logger.info('Webhook successful', {
            requestId,
            status: response.status,
            duration,
            response: response.data
        });
        
        return response.data;
        
    } catch (error) {
        logger.error('Webhook failed', {
            requestId,
            error: error.message,
            status: error.response?.status,
            responseData: error.response?.data
        });
        
        throw error;
    }
}

Performance Monitoring

Track webhook performance metrics:
class WebhookMetrics {
    constructor() {
        this.metrics = {
            totalRequests: 0,
            successfulRequests: 0,
            failedRequests: 0,
            averageResponseTime: 0,
            responseTimeSum: 0
        };
    }
    
    recordRequest(success, responseTime) {
        this.metrics.totalRequests++;
        this.metrics.responseTimeSum += responseTime;
        this.metrics.averageResponseTime = this.metrics.responseTimeSum / this.metrics.totalRequests;
        
        if (success) {
            this.metrics.successfulRequests++;
        } else {
            this.metrics.failedRequests++;
        }
    }
    
    getSuccessRate() {
        return this.metrics.totalRequests > 0 
            ? (this.metrics.successfulRequests / this.metrics.totalRequests) * 100 
            : 0;
    }
    
    getMetrics() {
        return {
            ...this.metrics,
            successRate: this.getSuccessRate()
        };
    }
    
    reset() {
        this.metrics = {
            totalRequests: 0,
            successfulRequests: 0,
            failedRequests: 0,
            averageResponseTime: 0,
            responseTimeSum: 0
        };
    }
}

const metrics = new WebhookMetrics();

// Log metrics every 5 minutes
setInterval(() => {
    console.log('Webhook Metrics:', metrics.getMetrics());
}, 5 * 60 * 1000);

Troubleshooting Common Issues

Symptoms: Requests hanging or timing outSolutions:
  • Increase timeout values in your HTTP client
  • Check network connectivity between your server and GoBlue
  • Implement retry logic with exponential backoff
  • Consider using async processing for webhook handlers
Symptoms: 429 status codes returned from GoBlueSolutions:
  • Implement batch processing to reduce request frequency
  • Add delays between requests
  • Use exponential backoff when retries are needed
  • Monitor your request volume and spread it over time
Symptoms: 400 status codes with validation error messagesSolutions:
  • Ensure phoneNumber field is always included
  • Validate phone number format before sending
  • Check that field names match your GoBlue form configuration
  • Verify JSON structure is correct
Symptoms: Webhook returns success but no messages in GoBlue queueSolutions:
  • Verify “Enable capturing” is turned on in your form
  • Check that “Auto Follow-up” is enabled
  • Ensure your form has a message template configured
  • Verify the correct form ID is being used in the webhook URL

Security Best Practices

Use HTTPS

Always use secure HTTPS connections for webhook endpoints

Validate Payloads

Verify webhook signatures and validate all input data

Rate Limiting

Implement rate limiting on your webhook endpoints

Error Handling

Never expose sensitive information in error messages

Next Steps