Logo
Search
API Docs

Node.js & Express Server Integration

Framework Integrations

Node.js & Express Server Integration: Handling Webhooks on a Vanilla Express Server

Overview

If you're running a plain Node.js server with Express (rather than a framework like Next.js), you can handle the core system's webhook events with a standard Express POST route. This page covers a worked handler for the common event types, the response rules your route needs to follow, and how to test it locally. If you're building on Next.js instead, see Next.js Server Integration.


Webhook Handler

Install Express if you haven't already:

npm install express

Then add a POST route that parses the event body and switches on the event type:

import express from 'express';

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

app.post('/api/webhook', async (req, res) => {
  const { type, call, timestamp } = req.body;

  console.log(`Webhook received: ${type} at ${timestamp}`);

  switch (type) {
    case 'call-started':
      console.log(`Call ${call.id} started with ${call.customer.number}`);
      break;

    case 'speech-update':
      console.log(`User said: ${req.body.transcript}`);
      break;

    case 'function-call': {
      const { functionName, parameters } = req.body.functionCall;
      const result = await processFunction(functionName, parameters);
      return res.json({ result });
    }

    case 'assistant-request': {
      // Return a transient assistant configuration or reject the call
      return res.json({
        assistant: {
          name: 'Inbound Receptionist',
          firstMessage: 'Hi there! How can I help you today?'
        }
      });
    }

    case 'call-ended':
      console.log(`Call ended. Duration: ${call.duration}s`);
      break;

    default:
      console.log(`Unhandled event type: ${type}`);
  }

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

app.listen(3000, () => console.log('Server listening on port 3000'));

async function processFunction(name, params) {
  // Your custom function logic here
  return { success: true };
}

Event Types & Response Codes

The route above handles the most common categories — call lifecycle (call-started, call-ended, call-failed), speech (speech-update, transcript), and assistant events (function-call, assistant-request, conversation-update). For the complete event catalog, see Webhooks & Events.

Your handler's HTTP status code controls delivery behavior:

Status CodeMeaning
200–299Success, event processed
400–499Client error, event rejected (not retried)
500–599Server error (will be retried)

See Webhook Delivery: Retries, Timeouts & Debugging for the full retry and 10-second timeout rules, including why you should queue heavy processing rather than block the response.


Testing Locally

Use the core system's CLI listener alongside a tunneling service like ngrok to send real webhook events to your local Express server:

# Terminal 1: Start your Express server
npm run dev  # Your app on localhost:3000

# Terminal 2: Start a tunnel
ngrok http 4242

# Terminal 3: Start the webhook listener
core-system listen --forward-to localhost:3000/api/webhook

Then update your Sulus dashboard webhook URL to the tunnel's public URL. The data flow is: core system > tunnel > core-system listen (port 4242) > your Express server (port 3000). Remember core-system listen only forwards — it does not create a public URL itself. For CLI authentication across multiple accounts, see CLI Key Rotation & Multi-Account.