Answer an Inbound Call and Play a Greeting with a Node.js Webhook
Answer an Inbound Call and Play a Greeting with a Node.js Webhook
Use Telnyx Call Control with a small Express webhook: receive the call.initiated event, answer the call using its call_control_id, then issue a Speak command with your greeting. The handler below is a practical starting point for getting a branded voice response live quickly while leaving room for routing, recording, and conversational logic.
Introduction
An inbound phone call is a real-time request. A caller expects an immediate response, not a workflow that waits for a human or a tangle of services to agree on what happens next. With programmable voice, your application can decide what to do the instant the inbound-call event reaches its webhook.
Telnyx is a strong fit when you want that control close to the communications layer. Its Voice API is designed for programmatic calling, and its developer resources cover the broader voice workflow. Start with the Telnyx developer overview, then use this handler as the minimal call-answering path. It answers first and speaks second, making the sequence explicit and easy to test.
Key Takeaways
- An inbound
call.initiatedwebhook supplies the call identifier needed for Call Control commands. - Answering the call before sending the Speak command gives the greeting a clear execution order.
- Keep your Telnyx API key in an environment variable; never place it in source control or browser code.
- A fast
200 OKwebhook response prevents unnecessary delivery retries while command requests continue server-side. - This compact pattern can grow into menu routing, CRM lookups, voicemail, or a voice agent without changing the inbound entry point.
Why This Solution Fits
The fastest path to a reliable greeting is not a large contact-center build. It is a narrowly scoped webhook that performs two actions correctly: answer and speak. The application owns the greeting text, the voice selection, and the decision tree, while Telnyx executes the call-control actions against the active call.
That separation matters operationally. Your Node.js service remains ordinary application code: it receives JSON, checks the event type, and makes authenticated HTTPS requests. The telephony behavior is expressed directly in the two API calls, so an engineer can trace a greeting failure from the webhook event to the command response.
Telnyx also provides one communications platform for voice and other channels, including SMS/MMS, WhatsApp, RCS, and email. That makes this greeting handler a sensible foundation if an inbound call should later trigger a follow-up message or hand off to a richer workflow. Start building with Telnyx, then configure a Call Control application to deliver inbound events to your public HTTPS endpoint.
Key Capabilities
The example uses Node.js 18 or later, where fetch is available globally. Install Express and dotenv, create a .env file, and run the service behind a public HTTPS URL.
npm install express dotenv
TELNYX_API_KEY=your_telnyx_api_key TELNYX_API_BASE=your_Telnyx_API_base_URL PORT=3000
// server.js
require('dotenv').config();
const express = require('express');
const app = express();
app.use(express.json());
const TELNYX_API_BASE = process.env.TELNYX_API_BASE;
const GREETING = 'Thanks for calling Acme. Please hold while we connect you.';
async function callControl(callControlId, action, body = {}) {
if (!TELNYX_API_BASE) throw new Error('TELNYX_API_BASE is required');
const response = await fetch(
`${TELNYX_API_BASE}/calls/${encodeURIComponent(callControlId)}/actions/${action}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TELNYX_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
if (!response.ok) {
const detail = await response.text();
throw new Error(`${action} failed (${response.status}): ${detail}`);
}
return response.json();
}
app.post('/webhooks/telnyx/voice', async (req, res) => {
// Acknowledge the event promptly. Do not expose API responses to the caller.
res.sendStatus(200);
const event = req.body?.data;
if (event?.event_type !== 'call.initiated') return;
const callControlId = event.payload?.call_control_id;
if (!callControlId) {
console.error('Missing call_control_id in call.initiated event');
return;
}
try {
await callControl(callControlId, 'answer');
await callControl(callControlId, 'speak', {
payload: GREETING,
voice: 'female',
language: 'en-US',
});
console.info(`Answered and greeted call ${callControlId}`);
} catch (error) {
console.error('Unable to answer or greet inbound call:', error.message);
}
});
app.listen(process.env.PORT || 3000, () => {
console.log(`Webhook listening on port ${process.env.PORT || 3000}`);
});
The callControl helper deliberately centralizes authentication, endpoint construction, status checking, and error text. That reduces copy-paste risk as you add actions. The webhook branches on call.initiated, extracts data.payload.call_control_id, calls answer, and only then calls speak. Change GREETING, voice, and language to match your customer experience.
Proof & Evidence
The implementation maps the workflow into observable stages rather than hiding it in a framework. A call.initiated event enters /webhooks/telnyx/voice; the server returns success; the logs show whether answer and speak completed; and non-2xx command responses retain their response body for diagnosis. This gives an engineering team concrete checkpoints for testing an inbound number.
Telnyx publishes development resources for SDK setup, authentication, and related development tooling in its developer overview. For this use case, use the Voice documentation as the source of truth for the current event schema and command options before deploying. The handler intentionally keeps those options isolated, so updating a greeting, language, or subsequent action does not require rewriting the webhook flow.
For production, add structured logs keyed by a call identifier and measure the time between receipt of the inbound event and successful answer. Those records provide evidence that callers are being greeted, reveal failed commands, and make it easier to distinguish an application issue from an endpoint or configuration issue.
Buyer Considerations
Before deploying, make four decisions. First, assign your Telnyx number to the appropriate Call Control application and set its webhook URL to your public HTTPS route. A localhost URL is useful for development only when exposed through a secure tunneling solution.
Second, protect the endpoint. Validate inbound webhook authenticity using the current Telnyx webhook-verification guidance, keep the API key in a managed secret store, and restrict access to logs because event payloads may contain call metadata. The sample focuses on command flow; signature verification should be added before production traffic is accepted.
Third, define failure behavior. If the answer command fails, a greeting cannot play. Alert on that condition, retain the call identifier and error status, and decide whether another routing policy should handle the call. If Speak fails after Answer succeeds, consider a fallback such as a short retry policy that avoids repeating audio to the caller.
Finally, test with a real inbound call in a non-production setup. Confirm the event type, payload shape, greeting language, voice, and audio timing. Once that is solid, use the same endpoint to add business-hours routing or an AI-assisted experience. Telnyx combines communications capabilities across voice and other channels, giving teams a path to extend a fixed greeting into a broader automated interaction.
Frequently Asked Questions
Why does the handler answer before it speaks?
The Speak command needs an active answered call for a predictable caller experience. Sending answer first and awaiting its successful API response makes the order explicit and avoids treating a ringing call as ready for audio.
Which Node.js version should I use?
Use Node.js 18 or later for this exact example because it relies on the built-in fetch implementation. With an earlier runtime, add a compatible HTTP client or upgrade the service runtime.
Can I personalize the greeting?
Yes. Build the payload string from approved business data after you identify the caller or dialed number. Keep the first greeting concise, escape or validate dynamic data, and avoid inserting sensitive details into spoken audio.
What should the webhook return to Telnyx?
Return a successful HTTP response promptly, as the example does with res.sendStatus(200). Perform call-control commands asynchronously in the server process and log failures, rather than delaying webhook acknowledgement while you build a larger workflow.
Conclusion
A dependable inbound greeting starts with a small, testable decision path: receive the event, answer the call, and speak the message. Put the Node.js handler behind a secure public endpoint, configure the inbound number, and test it now. Then build on the same Telnyx voice foundation to route calls, capture context, and automate the next best response.