Webhooks Guide for eCourtDate

What is eCourtDate?

eCourtDate is a cloud-native communications platform used by justice systems such as courts and prosecutors. Agencies can automatically send and receive two-way messages via texts (SMS), email, and voice calls.

Webhooks allows system admins to integrate eCourtDate's scheduling and messaging capabilities into their own applications and workflows.

eCourtDate Agency Required

To use this guide, you must be an authorized user with at least one assigned agency. Sign up for a free trial here ecourtdate.com/sign-up.

Getting Started

Webhooks are designed to provide real-time data about messages, events, and payments for machine-to-machine communication. You can use webhooks to notify an internal Teams channel about new payments or a kiosk system about new check-ins.

Step 1 - Create a Webhook

Login to the Console, then click Webhooks from the top navigation.

In the Add Webhook form, enter a name (you can change this later), choose a region, then click Add.


Step 2 - Customize Webhook

(optional) Choose the Agency to use for the webhook.

(required) Enter your webhook URL. This is the URL that we'll send webhook events to.


Step 3 - Use Webhooks

Webhooks can be used in connected systems to respond to real-time changes. Use our REST API to update data and trigger multilingual notifications.

Channels:

Sample Webhook

The following is a sample payload of an INBOUND_TEXTS webhook.


Securing Your Webhook

In production, we recommend using both IP Whitelisting and a Shared Secret to secure your webhook.

IP Whitelisting

To ensure that your webhook only receives requests from eCourtDate, whitelist the IP addresses that are shown in the Console when you edit your webhook.

There are two IP addresses to whitelist.

Shared Secret

To secure your webhook, we sign the payload using a shared secret. You provide us with this secret when setting up the webhook. We use it to generate a signature, allowing you to verify the request's authenticity.

You can set the shared secret when editing your webhook in the Console.

Note: the secret can be up to 24 characters.

Signature Header

The webhook request will include a header:

X-ECD-Signature: Contains the HMAC-SHA256 signature, generated using the shared secret.
Signature Verification
  • Retrieve the Signature: Extract the value of the X-ECD-Signature header from the incoming request.
  • Generate a Signature: Use the shared secret to compute the HMAC-SHA256 hash of the payload (raw request body). Ensure you use the exact payload content as received, then JSON encode the payload.
  • Compare Signatures: Compare the generated signature with the received signature. If they match, the request is authentic. Use a constant-time comparison function to prevent timing attacks.

Sample node.js code to generate a signature:

const crypto = require('crypto');
const secret = 'your-shared-secret';
const payload = JSON.stringify(request.body);
const signature = crypto.createHmac('sha256', secret).update(payload).digest('hex');
console.log('Generated Signature:', signature);

Testing Webhooks

When editing your webhook, you can test the webhook by clicking the Test Request button.

You should see the console output indicating the received response message and status code.

You can also test the webhook by sending or receiving a message to your Agency Phone or Agency Email.

In staging, outbound channels are not available. You can only test inbound channels.

Note: test requests use the Console's IP address. You can include the IP address in your whitelist or remove it after testing.


Sample Implementation Code

Node.js Implementation

Below is a secure Node.js implementation that validates webhook signatures and handles incoming webhook events, using only built-in Node.js modules.

Prerequisites
  • Node.js 18.0.0 or higher
  • Your webhook's shared secret from the Console
Project Setup

1. Create project directory and initialize:

mkdir ecd-webhook-demo cd ecd-webhook-demo npm init -y touch config.js app.js

2. Add the following code to config.js:

module.exports = { port: process.env.PORT || 3000, webhookSecret: process.env.WEBHOOK_SECRET || 'your-webhook-secret-here', // Add your whitelisted IPs from the Console allowedIPs: [ '123.45.67.89', '98.76.54.32' ] };

3. Add the following code to app.js:

const http = require('http'); const crypto = require('crypto'); const config = require('./config'); // Security headers middleware const securityHeaders = { 'Content-Security-Policy': "default-src 'none'", 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'DENY', 'X-XSS-Protection': '1; mode=block', 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains' }; // Verify webhook signature function verifySignature(signature, payload) { if (!signature) return false; const hmac = crypto.createHmac('sha256', config.webhookSecret); const calculatedSignature = hmac .update(payload) .digest('hex'); try { return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(calculatedSignature) ); } catch { return false; } } // IP whitelist check function isIPAllowed(ip) { return config.allowedIPs.includes(ip); } // Parse JSON safely function parseJSON(str) { try { return JSON.parse(str); } catch { return null; } } const server = http.createServer(async (req, res) => { // Add security headers Object.entries(securityHeaders).forEach(([key, value]) => { res.setHeader(key, value); }); // Only allow POST requests to /webhook if (req.method !== 'POST' || req.url !== '/webhook') { res.writeHead(404); res.end(); return; } // IP whitelist check const clientIP = req.headers['x-forwarded-for'] || req.socket.remoteAddress; if (!isIPAllowed(clientIP)) { res.writeHead(403); res.end(); return; } // Collect request body let body = ''; req.on('data', chunk => { body += chunk.toString(); // Basic protection against large payloads if (body.length > 1e6) req.destroy(); }); req.on('end', () => { // Parse JSON payload const payload = parseJSON(body); if (!payload) { res.writeHead(400); res.end(JSON.stringify({ error: 'Invalid JSON' })); return; } // Verify signature const signature = req.headers['x-ecd-signature']; if (!verifySignature(signature, body)) { res.writeHead(401); res.end(JSON.stringify({ error: 'Invalid signature' })); return; } // Log the webhook event console.log('Received webhook event:', { uuid: payload.uuid, channel: payload.channel, direction: payload.direction, created_at: payload.created_at }); // Handle inbound text message if (payload.channel === 'text' && payload.direction === 'inbound') { console.log('Processing text message:', { from: payload.from, to: payload.to, content: payload.content, token: payload.token, created_at: payload.created_at, status: payload.last_status }); // Add your text message handling logic here } // Send response res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ received: true, timestamp: new Date().toISOString() })); }); // Handle request errors req.on('error', (err) => { console.error('Request error:', err); res.writeHead(500); res.end(JSON.stringify({ error: 'Internal server error' })); }); }); // Start server server.listen(config.port, () => { console.log(`Webhook server running on port ${config.port}`); });
Security Features
  • No external dependencies
  • Built-in security headers
  • IP whitelisting
  • Signature verification using HMAC-SHA256
  • Timing-safe signature comparison
  • JSON parsing protection
  • Request size limiting
  • Error handling
Running the Server

1. Start the server:

node app.js

2. The server will listen for webhook events on http://localhost:3000/webhook

Note: For production use, ensure you:
  • Use HTTPS (Node.js https module or reverse proxy)
  • Set up proper logging
  • Use process manager (PM2 or systemd)
  • Update the allowed IPs list
  • Set the webhook secret via environment variable
Testing

You can test your webhook implementation using the Console's "Test Request" feature or by sending a real message to your Agency Phone/Email.



Technical Support

If you have any questions or issues while using eCourtDate's Webhooks, please contact our support team at help@ecourtdate.com. We're here to help.

We appreciate any feedback or suggestions to improve our Webhooks and technical resources.

© eCourtDate, Inc. All Rights Reserved.