Webhooks

Webhooks allow you to receive real-time notifications when events happen in ezylegal. Instead of polling the API, you can subscribe to events and receive HTTP callbacks when something changes.

How Webhooks Work

  1. You provide a webhook URL when creating a session (or configure a default in your Partner Dashboard)
  2. When an event occurs, ezylegal sends an HTTP POST request to your URL
  3. Your server processes the event and returns a 2xx response
  4. If delivery fails, ezylegal retries with exponential backoff

Quick Start

1. Create a Webhook Endpoint

Set up an endpoint on your server to receive webhooks:

// Express.js example
app.post('/webhooks/ezyfamily', express.json(), (req, res) => {
  const event = req.body;

  switch (event.event) {
    case 'session.completed':
      handleSessionCompleted(event.data);
      break;
    case 'session.pdf_ready':
      handlePdfReady(event.data);
      break;
    default:
      console.log('Unhandled event:', event.event);
  }

  res.status(200).send('OK');
});

2. Specify the URL When Creating Sessions

curl -X POST https://api.ezy-forms.com.au/v1/sessions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "form_type": "financial_statement",
    "customer_email": "[email protected]",
    "webhook_url": "https://yourapp.com/webhooks/ezyfamily"
  }'

3. Verify Webhook Signatures

Always verify that webhooks came from ezylegal:

const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

See Webhook Security for complete verification examples.

Event Types

| Event | Description | |-------|-------------| | session.accessed | Customer opened the form for the first time | | session.section_completed | A form section was completed | | session.completed | Form fully submitted | | session.pdf_ready | PDF document generated | | session.expired | Session expired without completion |

See Webhook Events for detailed payload examples.

Webhook Payload Format

All webhook payloads follow this structure:

{
  "event": "session.completed",
  "session_id": "sess_abc123xyz",
  "timestamp": "2024-01-16T14:22:00Z",
  "data": {
    // Event-specific data
  }
}

Headers

| Header | Description | |--------|-------------| | Content-Type | Always application/json | | X-Webhook-Signature | HMAC-SHA256 signature | | X-Webhook-ID | Unique delivery ID | | X-Webhook-Timestamp | Unix timestamp of delivery |

Retry Behavior

If your endpoint doesn't return a 2xx status code, ezylegal will retry:

| Attempt | Delay | |---------|-------| | 1 | Immediate | | 2 | 1 minute | | 3 | 5 minutes | | 4 | 30 minutes | | 5 | 2 hours | | 6 | 8 hours | | 7 | 24 hours |

After 7 failed attempts, the webhook is marked as failed and no more retries occur.

Retry Headers

On retry attempts, these headers are included:

| Header | Description | |--------|-------------| | X-Webhook-Retry-Count | Current attempt number (0-6) | | X-Webhook-Original-Timestamp | Timestamp of first attempt |

Best Practices

1. Respond Quickly

Return a 200 response as fast as possible. Process webhooks asynchronously:

app.post('/webhooks/ezyfamily', async (req, res) => {
  // Acknowledge immediately
  res.status(200).send('OK');

  // Process asynchronously
  await processWebhookAsync(req.body);
});

2. Handle Duplicates

Webhooks may be delivered more than once. Use the X-Webhook-ID header to deduplicate:

const processedWebhooks = new Set();

app.post('/webhooks/ezyfamily', async (req, res) => {
  const webhookId = req.headers['x-webhook-id'];

  if (processedWebhooks.has(webhookId)) {
    return res.status(200).send('Already processed');
  }

  processedWebhooks.add(webhookId);
  // Process webhook...
});

3. Verify Signatures

Always verify webhook signatures to ensure authenticity. See Security.

4. Use HTTPS

Webhook URLs must use HTTPS in production. HTTP is only allowed for localhost during development.

Testing Webhooks

Local Development

Use a tunneling service to test webhooks locally:

# Using ngrok
ngrok http 3000

# Your webhook URL becomes:
# https://abc123.ngrok.io/webhooks/ezyfamily

Manual Testing

You can trigger test webhooks from the Partner Dashboard:

  1. Go to SettingsWebhooks
  2. Click Send Test Webhook
  3. Select an event type
  4. View the delivery result

Troubleshooting

Webhook Not Received

  1. Check your endpoint is accessible from the internet
  2. Verify HTTPS is configured correctly
  3. Check firewall rules allow incoming requests
  4. Review webhook delivery logs in Partner Dashboard

Signature Verification Failing

  1. Ensure you're using the raw request body (not parsed JSON)
  2. Check your webhook secret is correct
  3. Verify you're using HMAC-SHA256

Processing Errors

  1. Check your server logs for exceptions
  2. Ensure your database can handle the incoming data
  3. Verify your async job queue is processing

Related Guides