Transcribe¶
Set up real-time speech-to-text transcription on a voice call using the VoIPBIN API. This walkthrough creates a flow that answers a call, starts transcription, speaks a greeting, and pauses — then shows how to receive transcription events via WebSocket and webhook.
Prerequisites¶
A valid authentication token (String) or accesskey (String). See Authentication.
A source phone number in E.164 format (e.g.,
+15551234567). Must be a number owned by your VoIPBIN account. Obtain available numbers viaGET /numbers.Your customer ID (UUID). Obtained from
GET https://api.voipbin.net/v1.0/customeror from your admin console profile.
Note
AI Implementation Hint
This guide uses virtual numbers (+899 prefix) which are free and do not require a provider purchase. The TTS talk action produces audio that is transcribed as direction: "out" (VoIPBIN to caller). If you speak into the call, your speech appears as direction: "in" (caller to VoIPBIN). The sleep action keeps the call alive for 30 seconds so you can observe transcription events in real time.
Step 1: Create a transcription flow¶
Create a flow that answers the call, starts transcription, speaks a greeting, and then pauses for 30 seconds. The transcribe_start action begins real-time speech-to-text; all subsequent audio (both TTS output and caller speech) is transcribed.
$ curl --request POST 'https://api.voipbin.net/v1.0/flows?token=<your-token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "quickstart transcribe flow",
"detail": "Answer, start transcription, speak greeting, pause 30s",
"actions": [
{
"type": "answer"
},
{
"type": "transcribe_start",
"option": {
"language": "en-US"
}
},
{
"type": "talk",
"option": {
"text": "Hello. This is a VoIPBIN transcription test. Everything you say will be transcribed in real time. Please speak now.",
"language": "en-US"
}
},
{
"type": "sleep",
"option": {
"duration": 30000
}
}
]
}'
The response includes the created flow with a server-generated id (UUID). Save this — you will need it in the next step:
{
"id": "b3f7a1d2-9c4e-4f8a-b6d1-2e5f8a3c7d90",
"name": "quickstart transcribe flow",
"detail": "Answer, start transcription, speak greeting, pause 30s",
"actions": [
{
"id": "a1b2c3d4-0001-0000-0000-000000000001",
"type": "answer"
},
{
"id": "a1b2c3d4-0001-0000-0000-000000000002",
"type": "transcribe_start",
"option": {
"language": "en-US"
}
},
{
"id": "a1b2c3d4-0001-0000-0000-000000000003",
"type": "talk",
"option": {
"text": "Hello. This is a VoIPBIN transcription test. Everything you say will be transcribed in real time. Please speak now.",
"language": "en-US"
}
},
{
"id": "a1b2c3d4-0001-0000-0000-000000000004",
"type": "sleep",
"option": {
"duration": 30000
}
}
],
"tm_create": "2026-02-18 10:00:00.000000",
"tm_update": "",
"tm_delete": ""
}
Key actions:
answer: Answers the incoming call. Required before any media actions on inbound calls.transcribe_start: Starts real-time speech-to-text. Thelanguagefield (String, BCP47 format) must match the speaker’s language (e.g.,en-US,ko-KR). See Supported Languages.talk: Generates TTS audio from thetextfield and plays it. This audio is transcribed asdirection: "out".sleep: Pauses flow execution fordurationmilliseconds (Integer).30000= 30 seconds. Keeps the call alive so you can speak and observe transcription.
Step 2: Create a virtual number and assign the flow¶
Create a virtual number (free, +899 prefix) and assign the transcription flow to handle inbound calls.
Create the virtual number:
$ curl --request POST 'https://api.voipbin.net/v1.0/numbers?token=<your-token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"number": "+899100000001"
}'
{
"id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
"number": "+899100000001",
"type": "virtual",
"call_flow_id": "00000000-0000-0000-0000-000000000000",
"message_flow_id": "00000000-0000-0000-0000-000000000000",
"name": "",
"detail": "",
"status": "active",
"t38_enabled": false,
"emergency_enabled": false,
"tm_create": "2026-02-18 10:01:00.000000",
"tm_update": "",
"tm_delete": ""
}
Save the number’s id (UUID) from the response.
Assign the flow to the number:
Update the number’s call_flow_id to point to the flow you created in Step 1. Replace <number-id> with the id from the response above, and <flow-id> with the flow id from Step 1:
$ curl --request PUT 'https://api.voipbin.net/v1.0/numbers/<number-id>?token=<your-token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"call_flow_id": "<flow-id>"
}'
{
"id": "d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90",
"number": "+899100000001",
"type": "virtual",
"call_flow_id": "b3f7a1d2-9c4e-4f8a-b6d1-2e5f8a3c7d90",
"message_flow_id": "00000000-0000-0000-0000-000000000000",
"name": "",
"detail": "",
"status": "active",
...
}
Now, any inbound call to +899100000001 will execute your transcription flow.
Note
AI Implementation Hint
Virtual numbers (+899 prefix) are free and routed internally within VoIPBIN. They do not require a provider purchase. If +899100000001 is already taken, try +899100000002 or search for available virtual numbers via GET /available_numbers?type=virtual.
Step 3: Subscribe to transcribe events via WebSocket¶
Before making the call, connect to the VoIPBIN WebSocket and subscribe to transcription events. This way you receive transcripts as they are generated during the call.
Connect to WebSocket:
wss://api.voipbin.net/v1.0/ws?token=<your-token>
Send a subscription message after connecting. Replace <your-customer-id> with your customer ID (UUID) obtained from GET https://api.voipbin.net/v1.0/customer:
{
"type": "subscribe",
"topics": [
"customer_id:<your-customer-id>:transcribe:*"
]
}
The wildcard * subscribes to events from all transcriptions under your account.
Python example:
import websocket
import json
token = "<your-token>"
customer_id = "<your-customer-id>"
def on_message(ws, message):
data = json.loads(message)
if data.get("event_type") == "transcript_created":
transcript = data["data"]
direction = transcript["direction"] # "in" = caller, "out" = VoIPBIN TTS
text = transcript["message"]
print(f"[{direction}] {text}")
def on_open(ws):
subscription = {
"type": "subscribe",
"topics": [
f"customer_id:{customer_id}:transcribe:*"
]
}
ws.send(json.dumps(subscription))
print("Subscribed to transcribe events. Waiting for transcripts...")
ws = websocket.WebSocketApp(
f"wss://api.voipbin.net/v1.0/ws?token={token}",
on_open=on_open,
on_message=on_message
)
ws.run_forever()
Step 4: Make a call to the virtual number¶
With the WebSocket connected, make an outbound call to the virtual number. The call’s destination is the virtual number you created; the source is any number owned by your account. In this quickstart scenario, the TTS greeting will generate direction: "out" transcripts. To also see direction: "in" transcripts (caller speech), you would need a SIP or WebRTC client connected to the call.
$ curl --request POST 'https://api.voipbin.net/v1.0/calls?token=<your-token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"source": {
"type": "tel",
"target": "<your-source-number>"
},
"destinations": [
{
"type": "tel",
"target": "+899100000001"
}
]
}'
{
"calls": [
{
"id": "c7d8e9f0-a1b2-3c4d-5e6f-7a8b9c0d1e2f",
"flow_id": "b3f7a1d2-9c4e-4f8a-b6d1-2e5f8a3c7d90",
"source": {
"type": "tel",
"target": "<your-source-number>"
},
"destination": {
"type": "tel",
"target": "+899100000001"
},
"status": "dialing",
"direction": "outgoing",
...
}
],
"groupcalls": []
}
The call dials the virtual number, which triggers the transcription flow: answer, start transcription, speak the greeting, and pause for 30 seconds.
Note
AI Implementation Hint
The source number must be a VoIPBIN-owned number (obtained from GET /numbers). The destinations target is the virtual number you created. Since the virtual number has a call_flow_id assigned, VoIPBIN executes the flow when the call is answered. No flow_id or actions field is needed in the call request — the number’s assigned flow handles everything.
Step 5: Receive real-time transcription events¶
Within seconds of the call being answered, the WebSocket begins delivering transcript_created events. The TTS greeting appears first (direction: "out"), followed by any speech from the caller (direction: "in").
Example WebSocket event:
{
"event_type": "transcript_created",
"timestamp": "2026-02-18T10:02:05.000000Z",
"topic": "customer_id:<your-customer-id>:transcribe:8c5a9e2a-2a7f-4a6f-9f1d-debd72c279ce",
"data": {
"id": "9d59e7f0-7bdc-4c52-bb8c-bab718952050",
"transcribe_id": "8c5a9e2a-2a7f-4a6f-9f1d-debd72c279ce",
"direction": "out",
"message": "Hello. This is a VoIPBIN transcription test. Everything you say will be transcribed in real time. Please speak now.",
"tm_transcript": "0001-01-01 00:00:08.991840",
"tm_create": "2026-02-18 10:02:05.233415"
}
}
Fields:
event_type(String): Alwaystranscript_createdfor new transcript segments.data.transcribe_id(UUID): The transcription session ID. Use this to query all transcripts for this session viaGET /transcripts?transcribe_id=<transcribe_id>.data.direction(enum string):"out"= audio from VoIPBIN to the caller (TTS output)."in"= audio from the caller to VoIPBIN (caller speech).data.message(String): The transcribed text.
If you run the Python WebSocket example from Step 3, you will see output like:
Subscribed to transcribe events. Waiting for transcripts...
[out] Hello. This is a VoIPBIN transcription test. Everything you say will be transcribed in real time. Please speak now.
[in] Hi, this is a test of the transcription feature.
Receive events via webhook (alternative)¶
Instead of WebSocket, you can receive transcription events via your customer webhook. Configure a webhook URI on your customer account — VoIPBIN sends HTTP POST requests for all events associated with your account, including transcription events:
$ curl --request PUT 'https://api.voipbin.net/v1.0/customer?token=<your-token>' \
--header 'Content-Type: application/json' \
--data-raw '{
"webhook_method": "POST",
"webhook_uri": "https://your-server.com/webhook"
}'
Once configured, transcription events (along with all other account events) are delivered to your webhook_uri as HTTP POST requests. There is no per-event-type filtering — your endpoint receives all events and must filter by event_type (e.g., transcript_created).
VoIPBIN sends a POST request to your endpoint each time a transcript segment is generated:
POST https://your-server.com/webhook
{
"event_type": "transcript_created",
"timestamp": "2026-02-18T10:02:05.000000Z",
"topic": "customer_id:<your-customer-id>:transcribe:8c5a9e2a-2a7f-4a6f-9f1d-debd72c279ce",
"data": {
"id": "9d59e7f0-7bdc-4c52-bb8c-bab718952050",
"transcribe_id": "8c5a9e2a-2a7f-4a6f-9f1d-debd72c279ce",
"direction": "in",
"message": "Hi, this is a test of the transcription feature.",
"tm_transcript": "0001-01-01 00:00:15.500000",
"tm_create": "2026-02-18 10:02:10.100000"
}
}
Note
AI Implementation Hint
Webhook delivery requires your uri endpoint to be publicly accessible and to return HTTP 200 within 5 seconds. For local development, use a tunneling tool (e.g., ngrok) to expose your local server. WebSocket is simpler for development and testing since it requires no public endpoint.
Step 6: Verify transcription results¶
After the call ends, you can retrieve the full transcript via the API. Use the transcribe_id (UUID) from the WebSocket or webhook events. In this flow-based scenario, no POST /transcribes call is made directly — the transcribe_id is generated internally when the transcribe_start action executes and is surfaced only via events. If you did not capture it from an event, you can also find it via GET /transcribes?resource_id=<call-id>.
$ curl --request GET 'https://api.voipbin.net/v1.0/transcripts?token=<your-token>&transcribe_id=<transcribe-id>'
{
"result": [
{
"id": "3c95ea10-a5b7-4a68-aebf-ed1903baf110",
"transcribe_id": "8c5a9e2a-2a7f-4a6f-9f1d-debd72c279ce",
"direction": "out",
"message": "Hello. This is a VoIPBIN transcription test. Everything you say will be transcribed in real time. Please speak now.",
"tm_transcript": "0001-01-01 00:00:08.991840",
"tm_create": "2026-02-18 10:02:05.233415"
},
{
"id": "06af78f0-b063-48c0-b22d-d31a5af0aa88",
"transcribe_id": "8c5a9e2a-2a7f-4a6f-9f1d-debd72c279ce",
"direction": "in",
"message": "Hi, this is a test of the transcription feature.",
"tm_transcript": "0001-01-01 00:00:15.500000",
"tm_create": "2026-02-18 10:02:10.100000"
}
]
}
The direction field distinguishes speakers: "out" is the TTS greeting played by VoIPBIN, "in" is the caller’s speech.
Troubleshooting¶
- No transcription events received:
Cause: WebSocket subscription topic does not match your customer ID, or subscription was sent before the connection opened.
Fix: Verify the customer ID in the topic matches your account (from
GET https://api.voipbin.net/v1.0/customer). Send the subscribe message only after theon_opencallback fires.
- 400 Bad Request on call creation:
Cause: The
sourcenumber is not owned by your account, or the number format is not E.164.Fix: Check your numbers via
GET /numbers. Ensure the source starts with+and contains only digits after it.
- Transcripts show only “out” direction, no “in”:
Cause: The call is outbound to a virtual number with no real caller speaking.
Fix: This is expected for the TTS-only portion. If you are connected to the call (e.g., via WebRTC or SIP phone), speak during the 30-second
sleepperiod to generate"in"transcripts.
- 404 Not Found on number update:
Cause: The number ID does not exist or belongs to a different customer.
Fix: Verify the number ID from the
POST /numbersresponse. UseGET /numbersto list your numbers.
For more details, see the full Transcribe tutorial and WebSocket tutorial.