Telnyx
Capture Telnyx Voice STT and TTS activity in Voxmith
Telnyx Voice uses the Voxmith voice webhook service. No Voxmith SDK import is required.
Data Sources
| Telnyx data | Who has it | Voxmith capture |
|---|---|---|
| In-call transcription | Telnyx webhook call.transcription |
Forward the webhook |
| REST/WebSocket STT text | Your app receives the transcript | Post kind: "stt" |
| TTS / speak text | Your app sends the text to Telnyx | Post kind: "tts" after Telnyx succeeds |
Minimal Server Integration
Keep Telnyx pointed at your app. Add one small forwarder for final STT and one for TTS:
POST https://api.voxmith.com/api/voice/v1/events/telnyx
X-Org-Id: <voxmith-org-id>Forward final transcription events:
{
"kind": "stt",
"data": {
"event_type": "call.transcription",
"payload": {
"call_session_id": "call-session-id",
"from": "+15551234567",
"transcription_data": {
"is_final": true,
"transcript": "hello from Telnyx"
}
}
}
}Record TTS where your app already calls Telnyx speak/playback:
{
"kind": "tts",
"text": "Sure, I can help with billing.",
"session_id": "call-session-id",
"user_id": "caller-id",
"mode": "in_call_playback"
}Copy-Paste Middleware
Use one server-side helper. STT can run inside the incoming Telnyx webhook handler. TTS must run beside the existing Telnyx speak/playback call because that is where your backend has the bot text.
const voxmithVoiceUrl = "https://api.voxmith.com";
const voxmithOrgId = process.env.VOXMITH_ORG_ID;
async function sendVoxmithTelnyx(body: Record<string, unknown>) {
await fetch(`${voxmithVoiceUrl}/api/voice/v1/events/telnyx`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Org-Id": voxmithOrgId!,
},
body: JSON.stringify(body),
});
}Forward final STT from your Telnyx webhook:
app.post("/webhooks/telnyx", async (req, res) => {
const payload = req.body;
const transcript = payload?.data?.payload?.transcription_data?.transcript;
const isFinal = payload?.data?.payload?.transcription_data?.is_final !== false;
const sessionId = payload?.data?.payload?.call_session_id;
if (payload?.data?.event_type === "call.transcription" && isFinal && transcript) {
await sendVoxmithTelnyx({
kind: "stt",
session_id: sessionId,
event_id: payload.data.id,
user_id: payload.data.payload.from,
agent_name: "telnyx_voice",
text: transcript,
});
}
res.sendStatus(200);
});Forward TTS where your product already speaks to the caller:
const replyText = await agentReply(userText);
await telnyx.calls.actions.speak(callControlId, { payload: replyText, voice: "female" });
await sendVoxmithTelnyx({
kind: "tts",
session_id: callSessionId,
event_id: crypto.randomUUID(),
user_id: callerId,
agent_name: "telnyx_voice",
text: replyText,
});Telnyx SDK Users
Using the Telnyx Node or Python SDK does not replace the STT webhook. Keep webhook_url
configured on the call so final call.transcription events still reach your backend.
Then wrap the SDK speak call to record TTS.
Node.js:
import Telnyx from "telnyx";
import crypto from "node:crypto";
const telnyx = new Telnyx({ apiKey: process.env.TELNYX_API_KEY });
async function speakWithVoxmith({
callControlId,
callSessionId,
callerId,
text,
}: {
callControlId: string;
callSessionId: string;
callerId: string;
text: string;
}) {
const eventId = crypto.randomUUID();
await telnyx.calls.actions.speak(callControlId, {
payload: text,
voice: "female",
command_id: eventId,
});
await sendVoxmithTelnyx({
kind: "tts",
session_id: callSessionId,
event_id: eventId,
user_id: callerId,
agent_name: "telnyx_voice",
text,
});
}Python:
import os
import uuid
import json
import urllib.request
from telnyx import Telnyx
telnyx = Telnyx(api_key=os.environ.get("TELNYX_API_KEY"))
def send_voxmith_telnyx(body: dict) -> None:
request = urllib.request.Request(
"https://api.voxmith.com/api/voice/v1/events/telnyx",
data=json.dumps(body).encode("utf-8"),
headers={
"Content-Type": "application/json",
"X-Org-Id": os.environ["VOXMITH_ORG_ID"],
},
method="POST",
)
with urllib.request.urlopen(request, timeout=5):
pass
def speak_with_voxmith(call_control_id: str, call_session_id: str, caller_id: str, text: str) -> None:
event_id = str(uuid.uuid4())
telnyx.calls.actions.speak(
call_control_id=call_control_id,
payload=text,
voice="female",
command_id=event_id,
)
send_voxmith_telnyx({
"kind": "tts",
"session_id": call_session_id,
"event_id": event_id,
"user_id": caller_id,
"agent_name": "telnyx_voice",
"text": text,
})Use the same session_id for STT and TTS events from the same call/session.
It can be the Telnyx conversation_id, call_session_id, or call_control_id; Voxmith turns non-UUID Telnyx IDs into stable dashboard UUIDs and keeps the original ID in metadata.
Pass a stable event_id when your middleware can retry the same STT/TTS event.
The Voxmith voice service stores final STT in memory for up to 10 minutes until the next TTS event for the same session_id.
That TTS write becomes one dashboard event with user speech in args and bot speech in result.
If a TTS event arrives without a queued STT, or after that queue expires, Voxmith records it as output-only.
Troubleshooting
- Only bot messages appear: TTS is arriving before final STT, more than 10 minutes after STT, or with a different
session_id. - No user input appears: you are forwarding partial STT only. Voxmith ignores STT where
finaloris_finalisfalse. - TTS appears in a separate conversation: pass the same Telnyx conversation or call ID as
session_id. - The accepted
session_idlooks different from the Telnyx ID: use the returnedconversation_idin dashboard/debug links. The original Telnyx ID is stored as metadata. - No events appear: confirm
X-Org-Idis set and your server can reach the Voxmith voice service. - Missing bot output: you are forwarding STT only. Post the TTS text after the Telnyx speak/playback request succeeds.
Telnyx references
- Telnyx Voice API webhooks
- Telnyx TTS overview
- Telnyx STT models
- Telnyx Node.js SDK
- Telnyx Python SDK
Next steps
- Conversations: review complete Telnyx calls.
- Events: inspect captured speech and agent responses.
- Intents: understand what callers wanted.