Outbound SIP (Telnyx) — declined calls are being retried up to 3x until answered; how do I stop the retries?

I’m using LiveKit Cloud with Telnyx outbound SIP trunk (FQDN) and I’m seeing repeated/duplicate calls whenever a call is declined.

What happens:
When a call is triggered and the callee declines it, the call is retried multiple times until it’s finally answered. In Telnyx’s SIP Call Flow Tool / CDR log for a single call, the attempt sequence looks like this:

  • Initial call → Declined

  • 1st retry → Declined

  • 2nd retry → Answered
    No more calls

On the receiving end I actually get the call multiple times — usually a follow-up/retry ring after I decline, up to 3 times, and sometimes 2 calls back to back.

The confusing part:
In the LiveKit / Telnyx dashboard it only shows one call, even though I’m physically receiving several rings/attempts.

Environment / setup:

  • LiveKit SIP with an outbound Telnyx SIP trunk

  • Transport: TCP

  • Region: US

What I want to understand:

  1. Is LiveKit re-sending the INVITE on a declined call, or is this Telnyx’s failover/retry behavior across its signaling IPs and routes?

  2. What SIP response code is LiveKit sending back on a decline? (Telnyx only stops retrying when it receives 603 Declined, 486 Busy, or 404 — anything else, like 480/5xx/timeout, triggers a retry.)

  3. How do I configure the LiveKit trunk / dispatch so a declined call is treated as final and not retried?

It isn’t LiveKit, and the retry codes explain why.

In pkg/sip/outbound.go the outbound INVITE has exactly one loop, and it only re-sends on an auth challenge (401 Unauthorized / 407 Proxy Authentication Required, capped at 5). Every other status returns immediately 400, 404, 480, 486, 488 fall through to INVITE failed, and anything else including 603 returns “unexpected status from INVITE response”. There’s no re-dial path, and no retry field exists on the trunk or on CreateSIPParticipant (only play_ringtone, ringing_timeout and max_call_duration).

On the response-code question that’s inverted for outbound. LiveKit is the caller here, so it receives the decline; it never sends one. The code comes from the callee’s device and their carrier.

Which is where the retries come from. Telnyx’s failover and retries doc lists 408, 480, 503, 504 and transport errors as triggering failover, and 180, 200, 404, 486, 603 as not triggering it, with the mechanism being “on failure, retry from IP2” (and for multiple routes, all routes via IP1 in order, then via IP2). Your symptom declined, re-rings, up to about 3 attempts matches that exactly.

So the thing to check is what code actually came back on the declined leg. Pull it from the Telnyx SIP Call Flow Tool: if it’s 486 or 603 there should be no retry, but if the handset is answering with 480 Temporarily Unavailable (some devices do on decline), or the leg is timing out to 408, you’re squarely in Telnyx’s failover set. That’s a Telnyx routing/destination-carrier matter rather than anything configurable on the LiveKit side.

@abidullahcs.uk

I understand it. But the status is showing “487” you can see the image. And This is happening for 3 different +91 phone numbers

487 changes things that’s not the callee declining. A decline comes back as 486 or 603. 487 Request Terminated is what you get when the caller cancels an INVITE that’s still ringing, so LiveKit is the side hanging up.

In pkg/sip/outbound.go, Close() sends CANCEL whenever the call is torn down before it’s answered:

  if c.inviteOk != nil {
      c.sendBye(ctx, headers)
  } else if c.invite != nil {
      c.sendCancel(ctx, headers)
  }

Two things reach it. ringingTimeout, which defaults to 3 minutes (defaultRingingTimeout = 3 * time.Minute) so it won’t fire on a decline timescale unless you’ve set it shorter on CreateSIPParticipant. Or the call context being cancelled sipSignal cancels on c.Disconnected() or c.Closed().

So the question is what’s ending the call on the LiveKit side while the phone is still ringing. Worth pulling your agent/job logs at the timestamp of one of those 487s to see what closed first. And since there’s no re-dial anywhere in livekit/sip and 487 isn’t in Telnyx’s failover set either whatever dials again is on your side.

@abidullahcs.uk

We are not setting any ringing timeout. However in livekit its showing sip request timeout. But we are getting the call.

api.CreateSIPParticipantRequest( room_name=ctx.room.name, sip_trunk_id=sip_trunk_id, sip_number=sip_number, sip_call_to=participant_phone, participant_identity=user_identity, krisp_enabled=False, wait_until_answered=True, )

If you look at the PCAP for SCL_4UDxCMFy2cEH (your screenshot shows it as - but if you load the page now you should see it), you’ll see the exchange from the LiveKit side. There is only a single INVITE and we eventually receive back a 200 followed by a 487. We are not doing any redialling on out side - it looks like that’s happening upstream. I’d :plus: the first responde from @abidullahcs.uk above.

@abidullahcs.uk
I’m also working on this with G Madhabananda Patra. I tested the same flow with an Indian number, and when I decline the call, I actually receive the call again multiple times — usually up to 3 retry rings, and sometimes I even get 2 calls back-to-back after declining

However, when I test with a +971 (Dubai/UAE) number, the issue doesn’t occur—the call is declined and no retry happens.

Based on this difference, could this be related to the response code or carrier behavior for Indian numbers? Do we need to change any LiveKit/Telnyx configuration, or is this something that needs to be handled on the carrier/routing side?

@Rishabh_Singh worth noting your call ended differently to the one @darryncampbell pulled the PCAP for. His showed 200 then 487, yours shows 486 User Busy, which is a plain decline with nothing cancelling it. Same symptom, two different endings.

Either way his point holds and it narrows nicely: one INVITE leaves LiveKit and you get three rings, so the duplication is happening between Telnyx and your handset rather than in your own dialling code. India failing while +971 does not fits that, since they take different routes.

Worth asking Telnyx whether their outbound route to India fans out across more than one downstream carrier before the 486 comes back. That is the one place those extra rings can be coming from, and only they can see it.