# Getting Started

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

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](https://synento.com/docs/guides/mcp.md).

## Prerequisites

- A Synento account ([sign up free](https://synento.com/signup): 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)

## Option A: Use the SDK (Recommended)

### 1. Install the packages

```bash
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`](https://www.npmjs.com/package/@synento/react) for hooks and
components, and [`@synento/player`](https://www.npmjs.com/package/@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.

```typescript
// 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

```typescript
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](https://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

```bash
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

```bash
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

```bash
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`](https://www.npmjs.com/package/@synento/client), which handles framing, codec
negotiation, decoding, backpressure and reconnection.

## Option C: Use the CLI

```bash
# 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:

```bash
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](https://synento.com/docs/guides/mcp.md) for other clients and what you approve.

## Next Steps

- Read [Core Concepts](https://synento.com/docs/concepts.md) to understand the architecture
- Browse the full [API Reference](https://synento.com/docs/api-reference.md)
- Try the copy-paste examples in [examples/](https://synento.com/docs/examples/web-quickstart.md)
- Set up webhooks for event-driven workflows — [Webhooks Guide](https://synento.com/docs/guides/webhooks.md)

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `SYNENTO_API_KEY` | Secret key. **Server-side only** — never expose it to a browser. | — |
| `SYNENTO_API_URL` | Base 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
