> Public copy of the ViewStream developer guide (also for AI agents). All guides: https://www.viewstream.co.il/developers/guide/index.md · API reference: https://api.viewstream.co.il/docs/

# Next.js and Vercel

`@viewstream/next` connects a Next.js (App Router) site to ViewStream and is ready for Vercel. Source, starter site and
full reference: `integrations/nextjs` (README) in the ViewStream repository.

| Import | What |
|---|---|
| `@viewstream/next/client` | `<ViewStreamPlayer>`, `<ViewStreamVertical>` (9:16 AI clips), `<ViewStreamUpload>` (direct browser upload) |
| `@viewstream/next/server` | `createViewStream()` (API helpers with Next cache tags), `createDelivery()` (Sites Delivery API), Connect |
| `@viewstream/next/webhook` | `viewstreamWebhook()` — signed webhook route that revalidates the affected pages |

## Set up on Vercel

1. Create an API key (Studio → **Integrations** → **API keys**) with `assets:read`, `clips:read`, `channels:read`,
   `ai:read`; add `assets:write` for uploads and `playback:sign` for protected streams.
2. Add the environment variables in Vercel: `VIEWSTREAM_API_KEY`, `VIEWSTREAM_TENANT`, `VIEWSTREAM_WEBHOOK_SECRET`
   (and optionally `VIEWSTREAM_CHANNEL`, `VIEWSTREAM_LANG`, `VIEWSTREAM_SITE_ORIGIN`). Never expose the key with a
   `NEXT_PUBLIC_` prefix.
   Uploads stay off unless you set `VIEWSTREAM_UPLOAD_PASSWORD` (editors sign in on `/upload`); in a real site put
   them behind your own login — a server action can be called by anyone who can reach the site.
3. Add a webhook (Studio → **Integrations** → **Webhooks**) to `https://<your-site>/api/viewstream/webhook` with
   `asset.published`, `clip.final` and `artifact.published`.

## Typical calls

```tsx
import { createViewStream } from '@viewstream/next/server';
import { ViewStreamPlayer } from '@viewstream/next/client';
const vs = createViewStream();

const video = await vs.assets.get(id);                                  // tag viewstream:asset:<id>
<ViewStreamPlayer tenant={await vs.tenant()} kind="vod" id={video.id} />

const hits = await vs.catchup.search(channelId, 'ריבית');               // items[].moments[{offset_s, text}]
const p = await vs.programmes.get(programmeId);                         // playback.catch_up
<ViewStreamPlayer tenant={t} mode="embed" src={p.playback.catch_up} fromProgrammeStart start={hits.items[0].moments[0].offset_s} />

const summary = await vs.programmes.summary(programmeId);               // summary, verified presenters, topics
const articles = await vs.articles.list();                              // published AI articles
const { src } = await vs.signPlayback({ channel: 'main' }, { viewer_ip }); // protected streams
```

```ts
// app/api/viewstream/webhook/route.ts
import { viewstreamWebhook } from '@viewstream/next/webhook';
export const POST = viewstreamWebhook({ secret: process.env.VIEWSTREAM_WEBHOOK_SECRET! });
```

The webhook route checks `X-VS-Signature` (HMAC-SHA256 over `"<X-VS-Timestamp>.<raw body>"`, 300 s window, constant
time) and revalidates the cache tags of the published video, clip or article, so the page updates at once.

## Uploads and Connect

- **Uploads** go from the browser straight to ViewStream storage in 64 MB parts with an upload-only token; your server
  starts and completes them (`startBrowserUpload` / `completeBrowserUpload`). Needs a key created through Connect.
- **Connect** lets each customer link their ViewStream account (PKCE, one-time code, scoped revocable key) instead of
  pasting a key: `pkce()`, `connectUrl()`, `exchangeCode()`, `disconnect()`.
