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:Copy
<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’sfunctions.php:
Copy
// 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');
goblue-contact.js):
Copy
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:Copy
// 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:Copy
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);
}
}
Copy
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:Copy
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:Copy
// 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:Copy
// 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:Copy
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:Copy
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:Copy
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:Copy
# 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:Copy
// 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:Copy
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:Copy
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
Webhook Timeouts
Webhook Timeouts
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
Rate Limiting Errors
Rate Limiting Errors
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
Data Validation Failures
Data Validation Failures
Symptoms: 400 status codes with validation error messagesSolutions:
- Ensure
phoneNumberfield is always included - Validate phone number format before sending
- Check that field names match your GoBlue form configuration
- Verify JSON structure is correct
Messages Not Appearing
Messages Not Appearing
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