Transport & Connectivity

Transport & Connectivity

Overshoot uses LiveKit as its default transport layer. When you create a stream, the server sets up a LiveKit room and gives you a URL and token to connect — no SDP, no TURN servers, no ICE candidates to manage.

Source Types

There are three ways to send video to Overshoot:

SourceAPI fieldWho manages transport?When to use
Native (default)Omit source, or { type: "native" }Server creates LiveKit room + tokensMost use cases — simplest option
WebRTC (legacy){ type: "webrtc", sdp: "..." }Client does WebRTC handshakeLegacy clients, backwards compatibility
LiveKit (user-managed){ type: "livekit", url: "...", token: "..." }User provides their own LiveKit roomAdvanced users with their own LiveKit infrastructure

Recommendation: Use native transport (the default). It's simpler, more reliable, and requires no transport configuration.

Native Transport

The default. Just create a stream without a source field:

POST /streams
 
{
  "mode": "frame",
  "processing": { "interval_seconds": 2.0 },
  "inference": {
    "prompt": "Describe what you see",
    "model": "Qwen/Qwen3.5-9B"
  }
}

The response includes a LiveKit URL and token:

{
  "stream_id": "abc-123",
  "livekit": {
    "url": "wss://livekit.overshoot.ai",
    "token": "<client JWT>"
  },
  "lease": { "ttl_seconds": 45 },
  "webrtc": null,
  "turn_servers": null
}

Connect to the LiveKit room and publish your video track — that's it.

WebRTC (Legacy)

The original transport. You create a WebRTC offer SDP locally and send it in the request:

{
  "source": { "type": "webrtc", "sdp": "v=0\r\n..." },
  ...
}

The response includes an SDP answer and TURN server credentials. You complete the WebRTC handshake yourself.

Note: WebRTC streams don't support automatic reconnection or deploy resilience. Consider migrating to native transport.

User-Managed LiveKit

For advanced users who run their own LiveKit infrastructure:

{
  "source": {
    "type": "livekit",
    "url": "wss://your-livekit-server.example.com",
    "token": "your-livekit-token"
  },
  ...
}

You're responsible for room creation, token generation, and publishing video.

SDK Integration

If you're using the Overshoot SDK, you don't need to think about transport at all. Both SDKs use native transport by default:

const vision = new RealtimeVision({
  apiKey: 'your-api-key',
  model: 'Qwen/Qwen3.5-9B',
  prompt: 'Describe what you see',
  source: { type: 'camera', cameraFacing: 'environment' },
  onResult: (result) => console.log(result.result)
})
 
await vision.start()
// SDK automatically: creates stream -> connects to LiveKit -> publishes video -> handles keepalive + token refresh

Manual Integration (Without SDK)

If you're integrating directly with the API:

  1. Create streamPOST /streams (no source field)
  2. Connect to LiveKit — use the livekit-client SDK with the returned URL and token
  3. Publish video — publish your video track to the room
  4. Receive results — connect to WS /ws/streams/{stream_id} as usual
  5. Keepalive — send keepalives as normal; each response includes a fresh livekit_token
import { Room, Track } from 'livekit-client'
 
// 1. Create stream
const res = await fetch('https://api.overshoot.ai/v0.2/streams', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    mode: 'frame',
    processing: { interval_seconds: 2.0 },
    inference: {
      prompt: 'Describe what you see',
      model: 'Qwen/Qwen3.5-9B'
    }
  })
})
const { stream_id, livekit } = await res.json()
 
// 2. Connect to LiveKit room
const room = new Room({ adaptiveStream: false, dynacast: false })
await room.connect(livekit.url, livekit.token)
 
// 3. Publish video
const stream = await navigator.mediaDevices.getUserMedia({ video: true })
const videoTrack = stream.getVideoTracks()[0]
await room.localParticipant.publishTrack(videoTrack, {
  source: Track.Source.Camera,
  simulcast: false,
})
 
// 4. Connect WebSocket for results (same as before)
// 5. Send keepalives — store the fresh livekit_token from each response

Token Lifecycle

Native streams use short-lived LiveKit tokens:

  • Initial token: Returned in the POST /streams response, 5-minute TTL
  • Refreshed tokens: Each keepalive response includes a fresh livekit_token
  • Tokens are always refreshed well before they expire via the regular keepalive cycle

The SDK handles token refresh automatically. If you're integrating manually, store the fresh token from each keepalive response — livekit-client will use it when it needs to reconnect internally.

Reliability & Reconnection

Native transport provides built-in reliability features:

Automatic Reconnection

Network transitions (WiFi to cellular), temporary disconnections, and ICE restarts are handled automatically. You don't need to implement any reconnection logic.

Deploy Resilience

Native streams survive server-side deploys with zero disruption to the client:

  • Your client stays connected to the LiveKit room throughout the deploy
  • On the next keepalive, the server automatically recovers and resumes inference
  • No action needed on the client side

This only works for native streams. Legacy WebRTC streams do not survive server restarts.

Automatic Cleanup

If a client disconnects without calling stop(), resources are cleaned up automatically.

Comparison

FeatureNative (default)WebRTC (legacy)LiveKit (user-managed)
Setup complexityMinimalSDP + ICE + TURNRequires own LiveKit infra
Automatic reconnectionYesNoDepends on your setup
Survives server deploysYesNoDepends on your setup
Token refreshAutomatic via keepaliveN/AYou manage tokens
LiveKit account neededNoNoYes