---
title: Passthru Applet
slug: exotel-agentstream/passthru-applet
docTags: 
createdAt: 2025-09-27T08:27:49.910Z
---



### Overview

The Passthru Applet sends call and streaming metadata from Voice Applet flows to your server, especially useful when executing:

- **Voicebot Applet (Bidirectional Streaming)**
- **Stream Applet (Unidirectional Streaming)**

Passthru sends additional stream-specific parameters as query parameters, enriching standard passthru behaviour.

Refer to the standard passthru documentation here: Working with Passthru Applet

This guide is an **extended version** with additional parameters for streaming flows.

::Image[]{src="https://s3.amazonaws.com/cdn.freshdesk.com/data/helpdesk/attachments/production/3081145718/original/Pb1RZvCmN2b9kVY_U2_x2o_mU3eyrR762g.png?1653799864" size="50" width="478" height="529" position="center" darkWidth="478" darkHeight="529" showCaption="false"}

### How It Works

Passthru makes an HTTP GET request to your URL, passing URL-encoded call and stream metadata.

**Example:**

:::CodeblockTabs
HTTP

```curl
GET https://yourserver.com/passthru-handler?
CallSid=56b1234567abcdef89abcdef12345678&
flow_id=voicebot-flow-xyz123&
Direction=inbound&
From=+918888000000&
To=+912233344455&
Stream[StreamSID]=6f048d2e897a0d2d4029560f3f541947&
Stream[Status]=completed&
Stream[Duration]=28&
Stream[RecordingUrl]=https://recordings.exotel.com/exotelrecordings/exotelt/6f048d2e897a0d2d4029560f3f541947.mp3&
Stream[StreamUrl]=wss://stream.customer.com/media&
Stream[DisconnectedBy]=bot
```
:::

Your backend must parse:

- URL query parameters
- Nested keys (e.g., Stream\[Status])
- JSON strings

### JSON Format (Raw Query String)

Sometimes, all parameters are sent in a single JSON string under the Stream key:

:::CodeblockTabs
JSON

```json
Stream={"StreamSID":"62xxx...",
"RecordingUrl":"https://recordings.exotel.com/...",
"Status":"completed",
"Duration":"28",
"StreamUrl":"ws://10.32.76.120:5007/media",
"DisconnectedBy":"user"}
```
:::

- Ensure your backend deserialises this string correctly.

### Handling Passthru Responses

Passthru requests can be handled synchronously or asynchronously:

- **Sync**: Exotel will immediately pass the call details synchronously during the call flow execution. It is possible only to make a binary decision using Passthru.
  - `200 OK → Option A`
  - `302 Found → Option B`
  - The person on call will wait until your URL responds.
- **Async**: Exotel will asynchronously pass the call details without interrupting the call flow execution. Use this mode when you don’t want to dynamically change flow execution. The user will not experience a delay.

### Streaming-Specific Fields (Flat Format)

| **Parameter**           | **Description**                                       |
| ----------------------- | ----------------------------------------------------- |
| Stream\[StreamSID]      | Unique streaming session ID                           |
| Stream\[Status]         | Status (completed, failed, cancelled)                 |
| Stream\[Duration]       | Stream duration (seconds)                             |
| Stream\[StreamUrl]      | WebSocket URL for streaming                           |
| Stream\[RecordingUrl]   | URL to recording (if enabled)                         |
| Stream\[DisconnectedBy] | Disconnector (user, bot, NA)                          |
| Stream\[DetailedStatus] | Present on failure (e.g., Streaming\_call\_throttled) |
| Stream\[Error]          | Error string (e.g., network failure or WS rejection)  |

**Note:** The above fields are added on top of standard passthru parameters.

### Throttling Scenario

Concurrency limit breaches trigger:

```json
Stream[Status]=failed
Stream[DetailedStatus]=Streaming_call_throttled
```

Implement retry or fallback strategies accordingly.

### Failure Handling

Below are the most common failure and disconnection scenarios captured in the passthru payload:

**Test 1: Call hung up during greeting**

**Expected:** Capture who disconnected and status

```json
{
"Status": "cancelled",
"DisconnectedBy": "user"
}
```

:::BlockQuote

:::

**Test 2: Invalid WebSocket URL**

**Expected:** Failure metadata including error, stream URL, and disconnector

```json
{
"StreamSID": "b68e46bca0fdexxxxx89643f6d81196d",
"RecordingUrl": "https://recordings.exotel.com/exotelrecordings/exotelt/b68e46bca0fdexxxxx89643f6d81196d.mp3",
"Status": "failed",
"Duration": "0",
"StreamUrl": "ws://xx.x.xx.xxx:50/media",
"Error": "3009 failed to establish ws conn dial tcp 10.1.76.120:50: connect: connection refused",
"DisconnectedBy": "NA"
}
```

**Test 3: Call cancelled after delay in streaming setup**

**Expected:** Accurate cancellation reason and disconnection origin

```json
{
"StreamSID": "6f048d2e897a0dxxxxx9560f3f541947",
"Status": "cancelled",
"DisconnectedBy": "user"
}
```

**Test 4: Call disconnected by bot after successful conversation**

**Expected:** Final stream status and session metadata

```json
{
"StreamSID": "f5dd49d7ac3xxxxx4b491ce948501947",
"Status": "completed",
"Duration": "29",
"StreamUrl": "wss://e33e-54-251-51-3.xxxxx-free.app",
"DisconnectedBy": "bot"
}
```

These structured outputs ensure consistent understanding of streaming lifecycle and failure context.

**Note:** DisconnectedBy values are standardized to: user, bot, NA.

Passthru logs enriched with detailed Error messages improve debugging and recovery mechanisms.
