Skip to main content

Real-Time Text (RTT/TTY-TDD) Accessibility on Live Voice Calls

Real-time text (RTT) is the modern successor to the telephone-typewriter (TTY/TDD) accessibility channel. It lets a caller or agent exchange text — often character-by-character — on a live call, alongside the audio for a hearing caller or instead of it for a deaf or hard-of-hearing caller. On Orbit, RTT is implemented as RFC 4103 (T.140) text frames carried on the same voice leg, and each frame is recorded per-leg so the agent’s RTT panel and the caller’s endpoint can each see the text as the other party types it. This guide covers how to arm the RTT channel on a live call, how agents and callers exchange text on it, the 2000-character frame ceiling, and how to roll the capability out as a tenant-owned accessibility commitment — not a platform mandate.

Prerequisites

  • A tenant with the voice/CCaaS pillar enabled.
  • The browser softphone or an embedded WebRTC client — RTT opens on the live call, not in a separate chat surface. See Set up the browser (WebRTC) softphone.

1. Arm the RTT channel on a live call

RTT is off for every call until you arm it. Per-call opt-in is deliberate — a contact center treats RTT as a conversation-level flag, not a blanket tenant setting. Three arm paths exist:
  • From the softphone footer. While a call is answered, click the Real-time text (TTY) toggle to open the panel. The toggle calls PATCH against the call’s RTT config with enabled: true and pins the panel for that call.
  • From an IVR or queue step. A flow that takes an inbound call to a queue can open the channel before an agent picks up by posting PATCH /api/v1/internal/rtt/<call-id>/config with { "enabled": true }. The internal HMAC or token credential reaches the config route — not the dashboard session token. Owner: Engineering.
  • From the agent’s outgoing send. When an agent types the first outgoing line, the send endpoint (POST /api/v1/voice/calls/<call-id>/rtt/send) accepts the frame and auto-flips the call’s RTT config to enabled on its path — a dashboard agent deliberately typing into the RTT panel is the per-call opt-in, and the softphone makes no separate arm call.
Failed writes return a real answer: a 404 when the call does not exist, a 409 with VOICE_RTT_NOT_ENABLED when the auto-arm path fails, and the structured enabled state in the response body on success — never a silent drop.

2. Exchange text on the live call

Once armed, every frame lands on both sides of the conversation:
  • Caller to agent. The caller’s TDD endpoint (or an app-side WebRTC client) streams text over the in-band T.140 leg. The softphone holds an open SSE connection to GET /api/v1/voice/calls/:callId/rtt/stream, so the agent sees the caller’s text arriving as it is typed — a per-frame event ordered by the call-relative timestamp.
  • Agent to caller. The agent’s send endpoint persists the outgoing line and pushes the same frame back over the caller’s endpoint. The body is { text, final }final: false while the agent is still typing (debounced), true on Enter. Each frame is recorded exactly once.
The same per-frame events drive any dashboard the tenant embeds — the RTT panel subscribes via SSE, not poll, which is what makes the sub-second latency target hold.

3. The 2000-character frame ceiling

A single RTT frame is capped at 2000 characters. The ceiling exists to defend against a malformed or hostile endpoint pushing a multi-kilobyte blob into one frame — it mirrors the same kind of cap on the live-interpretation module. Both the inbound caller frame and the outbound agent line are truncated at the frame endpoint, so the persisted row never violates the ceiling and the SSE event never ships a partial frame to the panel. Each persisted row is tagged source = "rtt_relay" and carries an is_final bit. Replay consumers (QA panels, post-call transcript exports) filter by that tag and sort by segmentStartMs — the same call-relative timeline the speech-to-text and post-call transcript sources use, so RTT rows interleave cleanly in the transcript view.

4. Replay the transcript

Two surfaces read the RTT receipt after the call:
  • GET /api/v1/internal/rtt/<call-id> returns the call’s RTT frames in playback order, cursor-paginated.
  • The dashboard’s call-details page shows the same receipt on the RTT panel — it reads the persisted rows with source = "rtt_relay", which keeps RTT frames from mixing into the post-call speech-to-text rows.
The rows inherit the existing transcript model: tenant-isolated, soft-deletable, and exportable through the same caption/export path the voice engine uses for every other source — WebVTT, SRT, or the JSON transcript the QA surface reads. An org with Zero Data Retention enabled skips RTT persistence entirely, exactly like the rest of the transcript sources.

5. Roll out as a tenant-owned accessibility gate

The FCC/ADA accessibility commitment for deaf and hard-of-hearing callers sits on each contact center — Orbit is the text-relay surface, not the accessibility mandate. A tenant usually adopts RTT in one of two postures:
  • Arm-it-on-demand. The softphone, the IVR, or the queue arms the channel per call once an agent flags the caller as a text-relay user. This is the default posture — RTT stays off until something opts in.
  • Arm-it-up-front. A flagged accessibility lane opens the config before the call lands on an agent, so every leg already has the channel on.
Track both postures by reading the per-call audit entries the API emits on the config write and each outgoing send — the supervisor can reconstruct when and how the channel opened after rollout.

Worked example — one flagged caller end to end

  1. The tenant’s CCaaS voice pillar is enabled and the agent’s softphone registers per the browser-softphone guide.
  2. Phone the tenant’s DID from an outside line. The supervisor flips the Real-time text (TTY) toggle as the call arrives, opening the panel.
  3. The agent types assistance into the panel; the caller’s text arrives over the SSE stream in the same panel.
  4. Complete wrap-up. The persisted rows with source = "rtt_relay" land on the call-details transcript panel, interleaved by timestamp with the speech-to-text rows, and clear on the retention sweep the org already runs.