Skip to main content

Configure Voice failover (Termii → Infobip)

Nerve places Voice OTP calls through a two-carrier chain: Termii first, then Infobip. If Termii fails or stalls, Infobip places the call within the same dispatch, so your code sees one event and one delivery receipt regardless of which carrier answered.

Both carriers are Bring-Your-Own: you add your own credentials in the dashboard. This how-to takes about five minutes.

1. Add the Termii credential

  1. Open the dashboard and go to Providers.
  2. Add a Termii provider and paste the API key from your Termii account. Voice uses the same Termii credential as SMS — there is no separate voice key.
  3. Set its priority to 1 so it is tried first.
  4. Click Test Connection to verify the credential before real traffic.

Termii carries the primary call. It does not accept a named voice profile, so voice_id is ignored on this leg — the carrier uses its default voice.

2. Add the Infobip credential

  1. Add an Infobip provider with your Infobip API key and base URL.
  2. Set its priority to 2 so it is the fallback.
  3. Click Test Connection.

Infobip is the failover leg and the only one of the two that honours voice_id, so name a TTS voice if you want a specific one.

Priority order is per workspace and per channel

priority controls failover across all channels. A credential used for both SMS and Voice keeps the same order. See Provider credentials for the full model.

3. What failover actually does

Voice is opt-in per event: a call is only eligible when the request supplies overrides.voice (the SDK's voice.send always does). Eligibility is not the same as being tried first:

  • Voice is attempted first only when the event priority is CRITICAL/HIGH or the request sets extraParams.voice_primary to "true"/"1".
  • For a routine (NORMAL/LOW) event without that flag, the cost order applies: SMS is tried first, and voice is the failover once the SMS attempt times out or fails.

Once voice is the channel being attempted:

  • Each carrier attempt gets a 5-second budget. A carrier that accepts the connection but never answers is treated as failed rather than left to ring.
  • The first success wins. The receipt names the carrier that placed the call as provider, so you never have to guess which leg ran.
  • If both carriers fail, the event ends FAILED with error_code DISPATCH_FAILED and the carriers that were tried in error_detail.

You do not implement this retry yourself — Nerve has already tried both carriers. Retrying a 202-accepted event only risks a duplicate call. See Failover & circuit breakers.

4. Provider differences to know

BehaviourTermiiInfobip
RolePrimaryFallback
voice_id (TTS profile)IgnoredHonoured
languageBCP-47 tagReduced to the primary subtag (en-USen)
CredentialAPI key, optional sender idAPI key, base URL, optional sender id

For the full request contract, see the Voice reference.

5. Verify a call

In Test Mode (nerve_sk_test_...) neither carrier is dialled: the worker records a normal timeline and returns a synthetic success, so you can exercise the chain without cost. When you are ready for a real call, send a CRITICAL event (or set extraParams.voice_primary=true) so voice is attempted first rather than ordered behind SMS, and confirm the receipt:

const receipt = await nerve.events.get(eventId);
console.log(receipt.channel, receipt.provider, receipt.status);

Read channel before provider: provider uses the same Termii/Infobip names for SMS as for voice, so provider: 'termii' alone does not prove a call was placed. Only once channel: 'voice' does the carrier tell you about the call — provider: 'termii' means the primary voice carrier answered, and provider: 'infobip' means voice failover ran, worth a look at the Termii credential or its account balance. A receipt with channel: 'sms' is an SMS dispatch, not a voice call.

Next