6 min read

Webhook Setup

Receive real-time notifications when submissions are graded, leads are scored, or routing decisions are made.

Overview

Webhooks provide real-time HTTP notifications when events occur in Grader.io. Use them to trigger workflows, update external systems, or notify team members instantly when high-value leads arrive.

Supported Events

Lead Scoring Events

  • submission.created
    - New form submission received
  • submission.scored
    - Lead has been evaluated and scored
  • submission.routed
    - Lead assigned to sales rep or team
  • submission.updated
    - Lead information modified

Grader Events

  • grader.activated
    - Grader went live
  • grader.paused
    - Grader stopped processing
  • grader.performance_alert
    - Conversion rate changed significantly

Integration Events

  • integration.connected
    - New platform connected
  • integration.failed
    - Connection error occurred
  • integration.sync_completed
    - Data synchronization finished

Setting Up Webhooks

1. Create a Webhook Endpoint

Your endpoint must accept POST requests and return a 200 status code:

// Node.js/Express example
app.post('/grader-webhook', (req, res) => {
  const { event, data, timestamp } = req.body;
  
  console.log(`Received ${event} at ${timestamp}`);
  
  // Process the webhook data
  switch (event) {
    case 'submission.scored':
      handleNewLead(data);
      break;
    case 'submission.routed':
      notifyAssignedRep(data);
      break;
    default:
      console.log('Unhandled event:', event);
  }
  
  res.status(200).json({ received: true });
});

2. Configure in Dashboard

  1. Navigate to Integrations - Dashboard → Integrations → Webhooks
  2. Add Webhook - Click "Create New Webhook"
  3. Enter Details:
    • Name: Descriptive name (e.g., "CRM Lead Sync")
    • URL: Your endpoint URL
    • Events: Select which events to receive
    • Secret: Optional signing key for security

3. Test Your Webhook

Use the webhook tester to verify your endpoint:

# Test payload example
{
  "event": "submission.scored",
  "webhook_id": "wh_abc123",
  "timestamp": "2023-10-15T14:30:00Z",
  "data": {
    "submission_id": "sub_xyz789",
    "score": 87,
    "grade": "hot",
    "form_data": {
      "email": "john.doe@acme.com",
      "company": "Acme Corporation", 
      "jobTitle": "VP Marketing"
    },
    "grader": {
      "id": "grader_123",
      "name": "Enterprise Lead Grader"
    }
  }
}

Webhook Payload Reference

Common Fields

All webhook payloads include:

FieldTypeDescription
event
stringEvent type identifier
webhook_id
stringUnique webhook identifier
timestamp
stringISO 8601 timestamp
data
objectEvent-specific data

Submission Events Payload

{
  "event": "submission.scored",
  "webhook_id": "wh_abc123",
  "timestamp": "2023-10-15T14:30:00Z",
  "data": {
    "submission": {
      "id": "sub_xyz789",
      "created_at": "2023-10-15T14:29:30Z",
      "score": 87,
      "grade": "hot",
      "source": "website_form",
      "form_data": {
        "email": "john.doe@acme.com",
        "firstName": "John",
        "lastName": "Doe",
        "company": "Acme Corporation",
        "jobTitle": "VP Marketing",
        "phone": "+1-555-0123",
        "website": "https://acme.com"
      }
    },
    "grader": {
      "id": "grader_123",
      "name": "Enterprise Lead Grader",
      "version": "1.2"
    },
    "routing": {
      "assigned_to": "rep_john_smith",
      "team": "enterprise_sales",
      "territory": "west_coast"
    }
  }
}

Security

Webhook Signing

Verify webhook authenticity using HMAC signatures:

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
    
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Express middleware
function validateWebhook(req, res, next) {
  const signature = req.headers['x-grader-signature'];
  const payload = JSON.stringify(req.body);
  
  if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  next();
}

Best Practices

  1. Use HTTPS - Always use secure endpoints
  2. Verify Signatures - Implement HMAC verification
  3. Idempotency - Handle duplicate deliveries gracefully
  4. Timeout Handling - Process webhooks quickly (<5 seconds)
  5. Error Logging - Log failures for debugging

Common Integration Patterns

CRM Synchronization

# Python/Django example
import requests
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

@csrf_exempt
@require_POST
def grader_webhook(request):
    data = json.loads(request.body)
    
    if data['event'] == 'submission.scored':
        submission = data['data']['submission']
        
        # Create lead in CRM
        crm_data = {
            'email': submission['form_data']['email'],
            'company': submission['form_data']['company'],
            'lead_score': submission['score'],
            'lead_source': 'Grader.io',
            'status': 'New'
        }
        
        # Send to CRM API
        response = requests.post(
            'https://api.crm.com/leads',
            json=crm_data,
            headers={'Authorization': f'Bearer {CRM_API_KEY}'}
        )
        
        if response.status_code == 201:
            print(f"Lead created in CRM: {submission['id']}")
        else:
            print(f"CRM error: {response.text}")
    
    return JsonResponse({'status': 'received'})

Slack Notifications

// High-value lead alerts
async function handleHighValueLead(data) {
  const submission = data.submission;
  
  if (submission.score >= 85) {
    const slackPayload = {
      channel: '#hot-leads',
      username: 'Grader.io',
      icon_emoji: ':fire:',
      text: `🔥 Hot Lead Alert! Score: ${submission.score}`,
      attachments: [{
        color: 'good',
        fields: [
          { title: 'Company', value: submission.form_data.company, short: true },
          { title: 'Contact', value: submission.form_data.email, short: true },
          { title: 'Title', value: submission.form_data.jobTitle, short: true },
          { title: 'Score', value: submission.score, short: true }
        ],
        actions: [{
          type: 'button',
          text: 'View in Grader.io',
          url: `https://app.grader.io/submissions/${submission.id}`
        }]
      }]
    };
    
    await fetch(process.env.SLACK_WEBHOOK_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(slackPayload)
    });
  }
}

Email Marketing Triggers

// PHP example for Mailchimp integration
function handleLeadRouting($data) {
    $submission = $data['submission'];
    $routing = $data['routing'];
    
    // Add to appropriate Mailchimp list based on score
    $listId = '';
    if ($submission['score'] >= 80) {
        $listId = 'hot-leads-list';
    } elseif ($submission['score'] >= 60) {
        $listId = 'warm-leads-list';
    } else {
        $listId = 'nurture-list';
    }
    
    $mailchimp = new MailchimpAPI($apiKey);
    $mailchimp->addListMember($listId, [
        'email_address' => $submission['form_data']['email'],
        'status' => 'subscribed',
        'merge_fields' => [
            'FNAME' => $submission['form_data']['firstName'],
            'LNAME' => $submission['form_data']['lastName'],
            'COMPANY' => $submission['form_data']['company'],
            'SCORE' => $submission['score']
        ],
        'tags' => ['grader-io', 'lead-' . $submission['grade']]
    ]);
}

Webhook Management

Monitoring Delivery

Track webhook performance in the dashboard:

  • Delivery Rate - Percentage of successful deliveries
  • Response Times - Average endpoint response time
  • Error Rates - Failed delivery attempts
  • Recent Activity - Last 100 webhook deliveries

Debugging Failed Deliveries

Common failure reasons:

  1. Endpoint Down - Target server not responding
  2. Timeout - Response took longer than 30 seconds
  3. Invalid Response - Non-200 status code returned
  4. Connection Error - DNS resolution or network issues

Retry Logic

Grader.io automatically retries failed webhooks:

  • Immediate Retry - After 1 second
  • Exponential Backoff - 1s, 2s, 4s, 8s, 16s, 32s
  • Maximum Attempts - 6 retries over ~1 hour
  • Dead Letter Queue - Failed webhooks available for 7 days

Webhook Endpoints Examples

Google Apps Script

// For Google Sheets integration
function doPost(e) {
  const data = JSON.parse(e.postData.contents);
  
  if (data.event === 'submission.scored') {
    const submission = data.data.submission;
    
    // Add to Google Sheet
    const sheet = SpreadsheetApp.getActiveSheet();
    sheet.appendRow([
      new Date(),
      submission.form_data.email,
      submission.form_data.company,
      submission.score,
      submission.grade
    ]);
  }
  
  return ContentService.createTextOutput(JSON.stringify({received: true}));
}

Zapier Integration

Create a Zapier webhook trigger:

  1. Create Zap - Choose "Webhooks by Zapier" as trigger
  2. Select Trigger - "Catch Hook"
  3. Copy URL - Use as webhook endpoint in Grader.io
  4. Test - Send test webhook from dashboard
  5. Configure Actions - Connect to 5,000+ apps

Troubleshooting

Webhook Not Firing

  1. Check Event Selection - Verify correct events are enabled
  2. Test Endpoint - Use external tools like curl or Postman
  3. Review Logs - Check webhook delivery history
  4. Verify URL - Ensure endpoint is publicly accessible

Payload Issues

  1. JSON Parsing - Ensure Content-Type is application/json
  2. Field Access - Check payload structure matches documentation
  3. Data Types - Verify expected data types (strings, numbers, booleans)
  4. Missing Fields - Some fields may be null or undefined

Performance Problems

  1. Response Time - Keep processing under 5 seconds
  2. Queue Processing - Use background jobs for heavy work
  3. Error Handling - Implement graceful failure recovery
  4. Resource Limits - Monitor memory and CPU usage

Next Steps

Once webhooks are configured: