Back to Blog
Calls that get answered: how Zend Voice works

Calls that get answered: how Zend Voice works

By Zend Team · July 22, 2026 · 6 min read

Some messages should be heard, not read. A ringing phone gets answered when an unread inbox does not, and for a lot of people, especially on basic handsets or with data switched off, a call is simply the surest way to reach them. Zend Voice places those calls from the same API as your other channels. Here is how it works.

Text to speech, or your own audio

A voice call carries one of two things. You either send text, which Zend reads aloud in a natural voice, or a voiceUrl, which points at an audio file you host. You send one or the other, never both.

await zend.voice.send({
  recipients: ['+233201234567'],
  text: 'Hello from Zend. Your verification code is 4 8 2 1.',
  voice: 'female',
  retry: true,
  fallback: { sms: true },
});

Text is spoken with a female or male voice, up to 600 characters. One nice touch: codes and long number sequences are read out digit by digit, so a verification code comes through as "four, eight, two, one" rather than "four thousand eight hundred twenty-one". That small thing is the difference between a code someone can act on and one they have to replay three times.

If you would rather play a recording, point at a hosted mp3 or wav, up to ten megabytes:

await zend.voice.send({
  recipients: ['+233201234567'],
  voiceUrl: 'https://cdn.yourbrand.com/reminder.mp3',
});

Reaching many people at once

A single request can carry up to 500 recipients, and duplicates are removed for you, so you do not have to clean the list first:

await zend.voice.send({
  recipients: ['0201234567', '233207654321', '+233209876543'],
  text: 'Your delivery is arriving today between 2 and 4 pm.',
});

Ghana number formats are accepted however you have them stored, whether that is a local 0 prefix or the international form.

When a call goes unanswered

A call is only useful if it connects, so Zend keeps trying, and then has a backup. By default retry is on, which re-attempts a call that is not picked up. And if it still goes unanswered, fallback.sms sends the message as a text instead, so the person still gets it:

await zend.voice.send({
  recipients: ['+233201234567'],
  text: 'Your appointment is tomorrow at 9 am.',
  retry: true,
  fallback: {
    sms: true,
    smsText: 'Reminder: your appointment is tomorrow at 9 am.',
    senderId: 'YourBrand',
  },
});

A missed call, in other words, is never a missed message.

Tracking every call

Because one request can fan out to many people, sending returns a batch rather than a single id. You get a batch_id and one message id per recipient:

{
  "batch_id": "voice_1f6a2b3c-4d5e-6789-abcd-ef0123456789",
  "recipients": 1,
  "message_ids": ["6884da240f0e633b7b979c00"],
  "status": "submitted",
  "credits_reserved": 0.35
}

From there you can poll the batch for delivery, or set a callbackUrl and let Zend push you each recipient's status as it changes, whether the call was answered, failed, or fell back to SMS. Either way, you can see exactly what happened to every call.

Get started

Place a test call to your own number, let it ring out, and watch the SMS fallback land.