One-way audio means the call connects, both sides think they talk, but audio travels in only one direction. Either the caller hears the agent and you hear nothing, or you hear the caller and the caller hears silence. Signalling is correct — the SIP dialog reached in_progress — so this is always a media-plane problem, not a connection problem. If the call never answers at all, see Call does not connect. Work from the symptom. The dead direction tells you which half of the media path to inspect first.

Which way is dead?

SymptomDead legLook here first
The caller hears the agent; you hear silenceUplink (caller → you)NAT / RTP source address — the far end cannot reach the address you advertised.
You hear the caller; the caller hears silenceDownlink (you → caller)A dropped AudioBridge handle, or an SDP a=sendonly / early-media direction.
Both connect, but one leg is noise, not silenceEitherA codec / payload-type mismatch on the negotiated leg.
In ClutchCall Voice, uplink and downlink are two separate media-over-QUIC tracks on the same call_sid: voice/<sid>/uplink (caller audio) and voice/<sid>/downlink (audio back to the caller). They fail independently. That is why one direction can die while the other continues to work. See Sessions, calls, tracks & streams for the track model.

Cause 1 — NAT / RTP path (the classic one-way)

This is the most common cause. The SDP you answered with advertises the address the far end must send RTP to. Behind cloud NAT, the engine binds on a private address but must advertise its public one. If the public RTP address is missing or wrong, the carrier streams caller audio into a black hole. Your egress still reaches the address they advertised. So the caller hears you, and you hear nothing.
CauseFix
The trunk advertises a private RTP IP in the SDP c= line. The private bind leaked into the answer.Set the trunk’s external RTP IP to the engine’s public address. The SDP then carries a reachable c=. The private bind stays internal.
Asymmetric RTP: the far end sends media from a different port than it signalled, so latching never occurs.The media plane latches egress to the source of the first inbound RTP packet. If no inbound packet arrives (see above), there is nothing to latch to. Fix the advertised address first.
A firewall or SBC in front of the trunk drops inbound RTP.Open the RTP port range to the carrier’s media IPs. Check that the SBC rewrites the SDP to its own public media address.
Confirm the diagnosis with the SIP/RTP packet trace viewer. If you see RTP leave the engine but zero inbound RTP on the caller leg, the far end sends to an unreachable address. That is an advertised-IP problem, not a routing problem. See SIP/RTP debugging.

Cause 2 — SDP direction attribute

An SDP media line carries a direction: sendrecv (both ways), sendonly, recvonly, or inactive. If early media (the 183 Session Progress) or the final answer negotiated anything other than sendrecv, the negotiation intentionally muted one direction.
CauseFix
The far end offered a=sendonly (announcement / hold-style early media) and never renegotiated to sendrecv.This is expected during ringback. If it continues after the answer, the caller PBX holds the leg. Check for a stray hold/re-INVITE in the trace.
The answer went out on the 183 with early media, but the 200 OK SDP differs.The answer sequence must be 100183 with SDP → 200 with the same SDP. A mismatched or SDP-less 200 desyncs the media direction. It can also trigger a carrier BYE.
The call is on hold — a re-INVITE flipped the leg to inactive/sendonly.Resume the leg. Look for a reinvited event in the call lifecycle.

Cause 3 — Codec / payload-type mismatch

When one leg is dead as silence, the cause is usually a path problem (Cause 1/2). When one leg is dead as noise or static, suspect the codec. The far end streams a payload type that you did not answer with. Frames arrive, but they decode to garbage. The bridge transcodes automatically across everything it negotiated: µ-law (PCMU, PT 0), A-law (PCMA, PT 8), and Opus. This includes µ-law ⇄ A-law between two mismatched G.711 legs. One-way noise means the far end sends a format that was never in the answer. There is nothing to transcode it to.
CauseFix
The carrier streams a payload type that is absent from your SDP answer.Offer both PCMU and PCMA, so any PSTN carrier can pick one. Let the bridge transcode to your chosen AudioCodec.
A static PT number is reused for a different codec (a non-standard carrier).Pin the codec on the trunk. Check that the negotiated rtpmap in the trace matches what the carrier sends.
See the Codec guide for which codec to pick per leg, and where transcode is free.

Cause 4 — the dropped AudioBridge handle

Keep the AudioBridge handle alive. The bridge holds the live publish/subscribe on voice/<sid>/{uplink,downlink}. If the handle goes out of scope, is garbage-collected, or is dropped, the tracks it owns tear down. The direction you published goes silent, while the other direction (served elsewhere) continues to work. This shows as one-way audio that starts a few seconds into a call, when the GC runs.
If the caller stops hearing you mid-call, but the SIP dialog is still up and your onUplink callback still fires, you almost certainly dropped the downlink publisher. Keep the bridge for the whole lifetime of the call. Close it explicitly on hangup.
In the synchronous cores (Rust, Go), the bridge closes on drop (RAII). When the value falls out of scope, both tracks tear down. Bind the bridge to a variable that lives as long as the call — for example, let bridge = voice.audio_bridge().attach(...)?; held in your session struct. Do not use a temporary.

Diagnose it in order

1

Confirm the dialog is actually up

Check that the call reached in_progress and is still there. If the call dropped, this is a connection problem, not one-way audio. Go to Call does not connect.
2

Establish which direction is dead

If only the caller hears you ⇒ the uplink is dead ⇒ start at Cause 1 (NAT/RTP path). If the caller hears silence ⇒ the downlink is dead ⇒ start at Cause 4 (bridge handle), then Cause 2 (SDP direction).
3

Read the packet trace

In the SIP/RTP trace viewer, look for RTP flow on each leg separately. No inbound RTP on a leg = the far end cannot reach your advertised address (Cause 1). Inbound RTP present but the audio is static = a codec mismatch (Cause 3). See SIP/RTP debugging.
4

Inspect the negotiated SDP

Check that the c= line carries a reachable address, and that the media line is sendrecv. A private c= IP or a sendonly/inactive direction is your answer.
5

Audit your bridge lifetime

If the dead direction is the one your code publishes, check that you hold the AudioBridge handle for the whole call. Call close() only on hangup.
6

Check MOS if audio is present but poor

One-way audio is binary: present or absent. If audio flows both ways but is choppy or clipped, that is a quality problem. See MOS, jitter & loss.
Human-agent downlink (browser softphones). When you hand a call to a human agent, the human hears the caller over a media-over-QUIC track in the browser softphone. That return-audio (downlink) path is still being hardened — the caller-audio origin publish is being bridged into the relay fan-out. If a browser human agent reports one-way audio (the agent can talk but cannot hear the caller), validate that leg in staging. Real SIP deskphone agents are not affected. Their media rides the normal RTP bridge. See AI ⇄ human handoff.

SIP/RTP debugging

Read per-leg SIP and RTP traces to find the dead direction.

Codec guide

Pick a codec per leg; where transcode is free and where mismatches bite.

Call does not connect

When the dialog never reaches in_progress in the first place.

Sessions, calls, tracks & streams

Why uplink and downlink are separate tracks that fail independently.

SIP trunking

Trunk fields for internal vs external RTP IP behind cloud NAT.

AI ⇄ human handoff

The human-agent downlink caveat, and how the return-audio path is wired.