---
"@context": https://schema.org
"@type": TechArticle
"@id": https://www.twilio.com/docs/video/troubleshooting/preflight-api#article
headline: Preflight API
description: How to access and use the Preflight API for testing connectivity to the Twilio Cloud.
url: https://www.twilio.com/docs/video/troubleshooting/preflight-api
inLanguage: en
dateModified: 2026-07-15T17:34:50.000Z
author:
  "@type": Organization
  name: Twilio Developer Education Team
publisher:
  "@type": Organization
  name: Twilio
---

# Preflight API

The Twilio Video JavaScript [Preflight API](https://sdk.twilio.com/js/video/releases/2.34.0/docs/PreflightTest.html) provides functions for testing connectivity to the Twilio Cloud. The API can identify signaling and media connectivity issues and provide a report at the end of the test. You can use the Preflight API in your Twilio Video applications to detect issues before a Participant joins a Video Room or as part of a troubleshooting page.

To check connectivity, the Preflight API creates two peer connections from the local user to Twilio's Signaling and TURN servers. It publishes synthetic audio and video tracks from one of those connections and ensures that the other connection receives the media on those tracks. After successfully verifying connectivity, it generates a report with information about the connection.

> \[!NOTE]
>
> The Preflight API doesn't test user bandwidth limitations. If you want to test client bandwidth limitations, use the `testMediaConnectionBitrate` method from the [RTC Diagnostics SDK](https://github.com/twilio/rtc-diagnostics).

## Access the Preflight API

The Preflight API is included with the Twilio Video JavaScript SDK in versions 2.16.0 and above. Versions 2.16.0 through 2.19.1 of the JavaScript SDK contain a beta version of the Preflight API. Version 2.20.0 and above of the JavaScript SDK contain the generally available Preflight API, which is no longer in beta.

You can include the JavaScript SDK in your application either by installing it with [Node Package Manager](https://www.npmjs.com/) (npm) or using the Twilio CDN.

See the [supported browsers](/docs/video/javascript#supported-browsers).

### NPM

Install the Video JavaScript SDK using npm:

```bash
npm install --save twilio-video
```

Then, you can start using the Preflight API in your application (note that the Preflight API is under the name `runPreflight`):

```javascript
const { runPreflight } = require('twilio-video');
```

#### Script tag

You can also copy `twilio-video.min.js` from the `twilio-video/dist` folder after npm installing it and include it directly in your web app using a `<script>` tag.

```html
<script src="https://my-server-path/twilio-video.min.js"></script>
```

Using this method, you can access the PreflightTest API like so:

```javascript
const runPreflight = Twilio.Video.runPreflight;
```

### CDN

You can also include the JavaScript SDK in your application from Twilio's CDN:

```html
<script src="https://sdk.twilio.com/js/video/releases/2.34.0/twilio-video.min.js"></script>
```

> \[!NOTE]
>
> You should make sure you're using the latest Twilio Video JavaScript SDK release. To find the CDN link for the most recent JavaScript SDK release, visit the [JavaScript SDK latest release documentation](https://sdk.twilio.com/js/video/latest/docs/).

Using the CDN, the JavaScript SDK will set a browser global that you can use to reference the Preflight API:

```javascript
const runPreflight = Twilio.Video.runPreflight;
```

## Use the Preflight API

### Example

The following example shows how to use the Preflight API to start a diagnostic connectivity test and handle events during the test.

```javascript
// import the Preflight API, which is called `runPreflight`,
// from the Twilio Video JavaScript SDK
const { runPreflight } = require('twilio-video');

// if you are using the Video JavaScript SDK via the Twilio CDN or
// a script tag, you would reference the Preflight API this way:
// const runPreflight = Twilio.Video.runPreflight;

// this assumes you have a function called getAccessToken
// to retrieve an Access Token from your server
const token = getAccessToken();

// run a preflight test, passing in an Access Token with
// a VideoGrant
const preflightTest = runPreflight(token);

// handle preflight test events

// while the test is in progress, the progress event fires
// whenever a particular PreflightProgress step completes
preflightTest.on('progress', (progress) => {
  console.log('preflight progress:', progress);
});

// if the test failed, the failed event fires and returns the error
// along with the partial test results it was able to collect
preflightTest.on('failed', (error, report) => {
  console.error('preflight error:', error);
  console.log('Received partial report:', report);
});

// if the test completed without error, the completed event fires
// and returns the preflight test report
preflightTest.on('completed', (report) => {
  console.log("Test completed in " + report.testTiming.duration + " milliseconds.");
  console.log(" It took " + report.networkTiming.connect?.duration + " milliseconds to connect");
  console.log(" It took " + report.networkTiming.media?.duration + " milliseconds to receive media");
});
```

### Start the diagnostic test

To run a diagnostic test with the Preflight API, call the [runPreflight() method](https://sdk.twilio.com/js/video/releases/2.34.0/docs/module-twilio-video.html#.runPreflight) and pass in an Access Token with a VideoGrant. This allows you to set up test connections to Twilio's servers.

When you run the test, you can pass in other [PreflightOptions](https://sdk.twilio.com/js/video/releases/2.34.0/docs/global.html#PreflightOptions). The options include setting your [preferred signaling `region` within the Twilio cloud](/docs/video/tutorials/video-regions-and-global-low-latency#regions-and-gll) (default: `gll`, which connects to the nearest signaling server based on latency) and the amount of time, `duration`, to run the test (default: `10000` ms, or 10 seconds).

#### Access Token

The Access Token you pass to the `runPreflight` must contain a [VideoGrant](/docs/iam/access-tokens#create-an-access-token-for-video).

Because the Preflight API is connecting to a test Video Room that the Preflight API sets up, any value you pass in for the `room` and `identity` fields in the VideoGrant will be ignored during the preflight test. The Access Token you use does require an `identity` field, but this identity can be any value for the purpose of the test.

### Listen for preflight test events

After you start running the preflight test with the `runPreflight` method, the test can generate three types of events. The code example above demonstrates how to listen for each event type.

* `progress`: The test is in progress and a [PreflightProgress step](https://sdk.twilio.com/js/video/releases/2.34.0/docs/global.html#PreflightProgress) in the test completed. This event passes the specific PreflightProgress step that completed.
* `failed`: The test failed with an error. This event passes back the error and any partially generated test results.
* `completed`: The test completed successfully and you can review the results. This event passes back the completed test report.

### Use PreflightProgress steps to track connectivity

While the Preflight Test is in progress, it will emit ProgressEvents indicating it completed specific connectivity checks. You can use these events to track the test progress and provide additional feedback to end users about their connections.

| **Name**                | **Description**                                                                                                                          |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| mediaAcquired           | Successfully generated synthetic tracks                                                                                                  |
| connected               | Successfully connected to Twilio's server and obtained TURN credentials                                                                  |
| mediaSubscribed         | The test connection successfully subscribed to media tracks                                                                              |
| mediaStarted            | Media flow was detected                                                                                                                  |
| dtlsConnected           | Established DTLS connection. This event will be not be emitted on Safari browsers.                                                       |
| peerConnectionConnected | Established a [PeerConnection](https://webrtc.org/getting-started/peer-connections). This event will not be emitted on Firefox browsers. |
| iceConnected            | Established an ICE connection                                                                                                            |

### Review the completed test report

The `completed` event will pass back a report containing the results from the successful test. The report will contain the following fields:

| Name                          | Description                                                                                                                                                                                                                                                            |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| testTiming                    | Time measurements for when the test started and ended (in Epoch time) and how long the test lasted in ms.                                                                                                                                                              |
| networkTiming                 | [Networking timing measurements](https://sdk.twilio.com/js/video/releases/2.34.0/docs/global.html#NetworkTiming) captured during the test.                                                                                                                             |
| iceCandidateStats             | An array containing the gathered ICE clients for STUN/TURN. Learn more about [STUN, TURN, and ICE here](/docs/stun-turn/faq#faq-what-is-nat).                                                                                                                          |
| selectedIceCandidatePairStats | Information about the ICE candidates that were used for the connection, such as the IP address, port, and protocol used.                                                                                                                                               |
| progressEvents                | A list of the [ProgressEvents](https://sdk.twilio.com/js/video/releases/2.34.0/docs/global.html#ProgressEvent) that occurred during the test.                                                                                                                          |
| stats                         | [RTC-related statistics](https://sdk.twilio.com/js/video/releases/2.34.0/docs/global.html#PreflightReportStats) captured during the test. Contains information about the average, minimum, and maximum jitter, round trip time (rtt), and packet loss during the test. |

### Stop an in-progress test

You can stop an in-progress test with the `stop()` method. This will stop the test and emit a `failed` event along with partial test results that completed up to the point that the test stopped.

## Interpreting results

The preflight test answers two questions: "Can this device connect to Twilio?" and "What will the call quality be like?" This section explains how to interpret the test results to answer both.

### Can you connect?

Use the combination of `progress` events and the `failed` event to determine connectivity:

| Outcome                                               | What it means                                    | Error code/message                                               | Recommended action                                    |
| ----------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------- |
| All progress events fire, `completed` emits           | The device can reach a Twilio TURN relay         | N/A                                                              | Connection is viable                                  |
| `failed` before `mediaAcquired`                       | Cannot generate synthetic tracks                 | N/A                                                              | Browser API issue (not a network problem)             |
| `failed` after `mediaAcquired` but before `connected` | Cannot reach the Twilio signaling server         | Code `53000`: "Signaling connection error"                       | Check firewall or proxy rules for WSS on port 443     |
| `failed` after `connected` but before `mediaStarted`  | TURN credentials acquired but media path blocked | Code `53405`: "Media connection failed or Media activity ceased" | Check UDP/TCP relay ports; may need TURN-TCP fallback |

> \[!NOTE]
>
> The `mediaAcquired` event does not access the user's camera or microphone. It generates synthetic audio/video tracks locally. This is a browser capability check, not a network check. The first network-dependent step is `connected`, which establishes a WebSocket connection to the signaling server and acquires TURN credentials.

The preflight test forces `iceTransportPolicy: 'relay'` on the publisher connection, meaning it only tests the Twilio TURN relay path. If the preflight succeeds, the real call (which can also use direct or STUN paths) also works. If the preflight fails, your network blocks relay traffic entirely.

### Use networkTiming for diagnostics

The `networkTiming` field provides duration measurements for each connection phase. The `connect.duration` value reflects multiple sequential round-trips (WebSocket handshake and TURN credential exchange), not a single RTT measurement. An expected baseline is 800-1000 ms for `connect.duration`.

Use these signals to identify specific problems:

* `connect.duration` > 5000 ms: Signaling server latency is high, possibly a distant region. Consider specifying a closer [region](/docs/video/tutorials/video-regions-and-global-low-latency#regions-and-gll).
* `media.duration` > 10000 ms: Media path is degraded, even if the connection succeeds.
* `ice.duration` > 5000 ms: ICE connectivity check is slow, possible symmetric NAT or firewall interference.

### What will quality be like?

The `stats` field in the completed report contains three metrics you can use to estimate call quality.

> \[!WARNING]
>
> Jitter is reported in **seconds** (per the WebRTC `inbound-rtp` specification), not milliseconds. A value of `0.015` means 15 milliseconds. RTT is reported in milliseconds and packet loss is reported as a percentage (0-100).

Use the following thresholds to interpret the stats:

| Metric                | Good     | Acceptable  | Poor    | Unit in report |
| --------------------- | -------- | ----------- | ------- | -------------- |
| RTT (round-trip time) | \< 100   | 100-300     | > 300   | milliseconds   |
| Jitter                | \< 0.030 | 0.030-0.100 | > 0.100 | seconds        |
| Packet loss           | \< 1     | 1-5         | > 5     | percent        |

The following reference implementation shows how to derive a quality estimate from the report:

```javascript
function estimateQuality(report) {
  const { rtt, jitter, packetLoss } = report.stats;
  if (!rtt || !jitter || !packetLoss) return 'insufficient-data';

  // Use average values for overall assessment
  const rttMs = rtt.average;          // already in milliseconds
  const jitterSec = jitter.average;   // in seconds per WebRTC spec
  const lossPercent = packetLoss.average;

  if (rttMs < 100 && jitterSec < 0.030 && lossPercent < 1) return 'excellent';
  if (rttMs < 200 && jitterSec < 0.050 && lossPercent < 3) return 'good';
  if (rttMs < 300 && jitterSec < 0.100 && lossPercent < 5) return 'acceptable';
  return 'poor';
}
```

### The selected ICE candidate pair

The `selectedIceCandidatePairStats` field shows how the connection was established. Because the preflight test forces `iceTransportPolicy: 'relay'` on the publisher connection, the selected pair always uses a relay candidate. The `relayProtocol` field indicates how constrained your network is:

| Local candidate          | Relay protocol | What it means                                                                                                                           |
| ------------------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `candidateType: "relay"` | `udp`          | TURN-UDP relay. Direct UDP connection to the Twilio TURN server. Allows for low-latency video and audio.                                |
| `candidateType: "relay"` | `tcp`          | TURN-TCP relay. UDP ports are blocked on this network. Expect higher latency and suboptimal quality.                                    |
| `candidateType: "relay"` | `tls`          | TURN-TLS relay. This is the most restrictive network path because UDP and plain TCP are blocked. TLS overhead adds the highest latency. |

The relay protocol is a proxy for how much overhead the connection carries. If you are on TURN-TLS, you generally experience worse quality than on TURN-UDP, all else being equal.

## Common patterns and recommendations

### Connection succeeds but quality is poor

```javascript
// report.stats shows:
// rtt: { average: 450, min: 200, max: 800 }
// jitter: { average: 0.085, min: 0.020, max: 0.150 }
// packetLoss: { average: 8.2, min: 0, max: 15 }
```

High RTT suggests the user is far from the nearest Twilio media region, or their network has congestion. High packet loss causes visible artifacts. Try specifying a closer `region` in Room options, or advise the user to switch to a wired connection.

### Connection uses TURN-TLS relay

```javascript
// selectedIceCandidatePairStats.localCandidate shows:
// candidateType: "relay", relayProtocol: "tls"
```

The network blocks UDP and plain TCP, forcing the most restrictive relay path. Video works, but exclusively through TURN-TLS relay, which adds latency and results in degraded call experience. This can happen in corporate networks with strict firewalls where the Twilio media IP blocks and ports are not allowlisted. See [Video IP addresses and firewall rules](/docs/video/ip-addresses) for the required network configuration.

### Connection fails at media path

```javascript
// progressEvents shows: mediaAcquired, connected, mediaSubscribed, but no mediaStarted
// error: "Media connection failed or Media activity ceased"
```

The TURN relay connected but the media path failed. This may indicate deep packet inspection interfering with encrypted traffic, or UDP being blocked entirely.

## Decision framework

Use this flowchart to decide what to tell your users based on the preflight result:

```text
Run preflight test
       |
       v
  completed? ──No──> Check error + progressEvents
       |                    |
      Yes              Which stage failed?
       |                    |
       v              [See connectivity table]
  Check stats
       |
       v
  All "Good"? ──Yes──> "Ready for Video"
       |
      No
       |
       v
  Which metric is poor?
       |
  ┌────┼────────┐
  v    v        v
 RTT  Jitter  Packet Loss
  |    |       |
  v    v       v
 Suggest   Suggest    Suggest
 closer    wired      wired or
 region    connection less congested
                      network
```

## Limitations

The preflight test has the following limitations:

* **No bandwidth testing**: The preflight test only tests connectivity and path quality. It does not measure available bandwidth. Use the [RTC Diagnostics SDK](https://github.com/twilio/rtc-diagnostics) `testMediaConnectionBitrate` method for bandwidth testing.
* **Synthetic media only**: The test does not access the user's real camera or microphone. It uses synthetic audio and video tracks to verify the media path.
* **Single pub/sub pair**: The test does not replicate the exact topology of a multi-party room. It tests a single publish/subscribe pair through the Twilio TURN server.
* **TURN-relay only**: The test uses `iceTransportPolicy: 'relay'`, so stats reflect the relay path. In a real call, if a direct connection is available, quality may be better than what the preflight reports.
* **Test duration**: The default duration is 10 seconds. The test collects stats once per second, yielding approximately 9 data points. Longer durations (configurable through `options.duration` in milliseconds) produce more samples and are better at detecting intermittent degradation, but increase user wait time.

## Video Diagnostics Application

Twilio offers an open-source [Video Diagnostics Application](https://github.com/twilio/twilio-video-diagnostics-react-app) that is built using the Preflight API and the [RTC Diagnostics SDK](https://github.com/twilio/rtc-diagnostics). You can deploy and explore this application to see the different functionality of these diagnostic APIs in action. The application tests participants' device and software setup, connectivity with the Twilio Cloud, and network performance. It provides users feedback about their network quality and device setup, and also includes recommendations for improving their video call quality.
