Synento docs

Getting Started

Get from zero to your first live video stream in under 5 minutes.

View as Markdown

Get from zero to your first live video stream in under 5 minutes.

Using a coding agent? Connect it to Synento over MCP and it can do all of this for you — including proving the video works. See Connect your agent.

Prerequisites

  • A Synento account (sign up free: 3,000 participant-minutes a month, no card)
  • A server-side key — an sk_live_ API key from the dashboard or CLI, or an rk_live_ restricted key (one project; enough for everything on this page)

1. Install the packages

npm install @synento/node      # server-side: create sessions, mint tokens
npm install @synento/client    # browser: publish and receive live audio/video

React applications can use @synento/react for hooks and components, and @synento/player for embedded replay.

2. Mint connection tokens on your server

Your API key is a server-side secret. It never goes to the browser. Instead, your backend exposes one small route that creates a session and mints a short-lived connection token for the user asking to join.

// app/api/synento/route.ts  (Next.js App Router)
import { createSynentoClient, resolveWsUrl } from "@synento/node";

// Reads SYNENTO_API_KEY and SYNENTO_API_URL from the environment.
const synento = createSynentoClient();

export const dynamic = "force-dynamic"; // or the route is cached and serves a stale token

export async function POST(request: Request) {
  const { sessionId, userId } = await request.json();

  const token = await synento.createConnectionToken(sessionId, { userId });

  return Response.json({
    token: token.accessToken,
    wsUrl: resolveWsUrl({
      apiBaseUrl: process.env.SYNENTO_API_URL ?? "https://api.synento.com",
      requestOrigin: request.headers.get("origin"),
      sessionId,
      userId,
    }),
  });
}

Use resolveWsUrl rather than assembling the URL yourself — it handles the loopback-hostname case that otherwise makes local development fail in a way that produces no error at all, just a call that never connects.

3. Join from the browser

import { SynentoRoom } from "@synento/client";

const room = new SynentoRoom({
  userId: "alice",
  // Re-invoked on every reconnect, so tokens rotate automatically.
  getToken: async () => {
    const res = await fetch("/api/synento", {
      body: JSON.stringify({ sessionId, userId: "alice" }),
      method: "POST",
    });
    return res.json(); // { wsUrl, token }
  },
});

room.on("participantsChanged", (ids) => console.log("in the room:", ids));
await room.connect();

// Publish. The second argument is optional — pass your local preview element
// if you have one. A stream with no video tracks publishes audio only.
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
await room.publishCamera(stream, previewVideoEl);

Media travels as binary frames, not JSON — @synento/client handles the framing, decoding and reconnection. Do not open a raw WebSocket and try to JSON.parse messages off it.

Recording needs no code. The session's durable streams are the recording, so there is no start-recording call and no recording flag.

4. See it running

demo.synento.com is a two-way call built on exactly these packages. Open it in two tabs, join the same session, then leave and replay it: the recording exists the moment the call does.

Option B: Use HTTP API Directly (cURL)

1. Create a project

curl -X POST https://api.synento.com/v1/projects \
  -H "Authorization: Bearer sk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Project"}'

2. Create a session

curl -X POST https://api.synento.com/v1/projects/{projectId}/sessions \
  -H "Authorization: Bearer sk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"name": "Demo Session"}'

3. Get a connection token

curl -X POST https://api.synento.com/v1/sessions/{sessionId}/connection \
  -H "Authorization: Bearer sk_live_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "viewer-1"}'

4. Connect to WebSocket streaming

Once you have the access_token from step 3, open a WebSocket. Prefer sending the token as a subprotocol, so it stays out of URLs and access logs:

wss://api.synento.com/ws?session={sessionId}&user_id=viewer-1
Sec-WebSocket-Protocol: synento.v1, synento.token.{accessToken}

The query-string form (&access_token=...) is still accepted, but logs the token.

What travels over that socket is binary, in a self-describing frame format — not JSON. Unless you are implementing a client from scratch, use @synento/client, which handles framing, codec negotiation, decoding, backpressure and reconnection.

Option C: Use the CLI

# Login with your API key
npx synento login sk_live_xxxxx

# Create a project (interactive or with name argument)
npx synento create-project "My Project"

# Create a session (requires project ID)
npx synento create-session "Demo Session" -p {projectId}

Option D: Let your coding agent do it

Connect your agent to Synento's MCP server and ask it to add video to your app:

claude mcp add --transport http synento https://api.synento.com/mcp

It mints a restricted key for your .env, writes the server route and the browser code above, and then checks the recording with verify_session to confirm camera and microphone actually arrived. See Connect your agent for other clients and what you approve.

Next Steps

Environment Variables

VariableDescriptionDefault
SYNENTO_API_KEYSecret key. Server-side only — never expose it to a browser.—
SYNENTO_API_URLBase URL of the Synento API.https://api.synento.com

Both are read automatically by createSynentoClient() when you pass no arguments.

Troubleshooting

"Connection refused" on WebSocket

  • If you set SYNENTO_API_URL, check it is https://api.synento.com
  • Check that the connection token has not expired

"401 Unauthorized" on API calls

  • Verify your API key starts with sk_
  • Check that the Authorization header uses Bearer <key> (no space before the key)

Sessions not appearing in replay

  • Ensure the session has ended (replay reads completed sessions)
  • Check that events were actually sent during the stream

On this page