By Hakim Team7 min read

Arabic IVR and phone voice agents: audio formats, latency and fallbacks

Building an Arabic phone agent or IVR: realtime transcription of 8 kHz calls, speech at telephony sample rates, dialect-matched voices, and fallbacks callers never notice.

EngineeringProduct

Phone lines are where Arabic voice AI meets its hardest conditions: 8 kHz audio, background noise, callers who switch between dialect and English, and zero patience for a pause. This guide covers how to build an Arabic IVR or phone voice agent with Hakim, from audio formats to fallbacks.

The loop

A phone agent runs the same loop on every turn:

  1. The caller speaks. Audio streams to speech-to-text.
  2. Your logic or language model decides the reply.
  3. The reply streams to text-to-speech and back to the caller.

Callers judge the whole loop, so every step has to be fast and every step has to handle phone audio.

Listening: realtime STT on phone audio

Open wss://api.tryhakim.ai/v1/audio/transcriptions/stream and configure the session for telephony:

{
  "type": "session.update",
  "session": {
    "model": "hakim-arab-v2",
    "language_hints": ["ar", "en"],
    "input_audio_format": "mulaw",
    "input_sample_rate": 8000,
    "trim_leading_silence": true,
    "vocabulary": {
      "terms": ["فاتورة", "eSIM", "رقم الحساب"],
      "notes": "A telecom support call about bills and mobile plans."
    }
  }
}
  • Send μ-law at 8 kHz as it is. Most telephony stacks produce it, and there is no need to convert.
  • Use language_hints: ["ar", "en"] because callers mix in English words for products and technology.
  • Turn on trim_leading_silence. Calls often start with seconds of ringing or silence, which wastes time and makes vocabulary less reliable.
  • Add your vocabulary: plan names, product names, branch names. See the vocabulary guide.
  • Commit on pauses. When your turn detection decides the caller has finished, send input_audio_buffer.commit and act on the transcription.done text.

Speaking: TTS for the phone

For prompts you know in advance, generate them once with POST /v1/audio/speech at the phone's sample rate:

curl https://api.tryhakim.ai/v1/audio/speech \
  -H "Authorization: Bearer $HAKIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"hakim-fast-v1","input":"أهلًا بك في خدمة العملاء. كيف أقدر أساعدك؟","voice":"VOICE_ID","response_format":"wav","sample_rate":8000}' \
  --output welcome.wav

For dynamic replies, use the realtime TTS socket at wss://api.tryhakim.ai/v1/audio/speech/stream with Hakim Saree' v1.3 (hakim-fast-v1), which starts audio in about 113 ms p95. It returns 24 kHz PCM, which your media server resamples and encodes for the line (usually G.711 μ-law), the same way it handles any other audio source. Send one speech.create per sentence as your language model produces them, and set trailing_silence_ms: 0 so sentences join naturally.

Voices that fit the line

Pick a voice whose dialect matches your callers. A Saudi bank line should sound Saudi; an Egyptian delivery service, Egyptian. A calm, clear "customer service" voice usually works better on the phone than a highly expressive narrator. If you have a brand voice, clone it with consent and use it everywhere.

Fallbacks callers never notice

Networks fail. Plan for it:

  • Pre-render your top prompts (welcome, hold, transfer, goodbye) as static files at 8 kHz and switch to them if a stream fails, instead of leaving silence.
  • Keep sessions open across holds. A realtime STT session with no incoming audio stays open for up to five minutes, with its vocabulary intact.
  • Retry once, then degrade. If a synthesis request fails, retry once and then fall back to a pre-rendered "one moment please".

Compliance

Phone audio is personal data, and in banking, telecom and government it is often regulated. Hakim processes audio in-region in the UAE, Saudi Arabia, Qatar or Frankfurt, with a signed DPA for enterprise customers. The compliance page lists the detail.

The streaming guide includes a full voice-agent recipe, and the LiveKit and Pipecat plugins give you the loop above with a few lines of configuration.

Read next