---
title: Accessing Previews
description: Learn how to display v0 chat previews from the Platform API v2
type: guide
---

# Accessing Previews



Use [`chats.getPreview`](/api/v2/reference/chats/get-preview-url) to display the live preview for a chat in your own product. The endpoint returns a preview URL plus a short-lived token for accessing that URL.

## 1. Get the preview URL [#1-get-the-preview-url]

Call `chats.getPreview` with your v0 API key. If the preview is still starting, `preview` is `null`; poll until it is present.

```typescript
import { v0 } from 'v0-sdk'

const { preview } = await v0.chats.getPreview({
  chatId: 'chat_abc123',
})

if (!preview) {
  // Try again shortly.
  return
}
```

## 2. Cache preview [#2-cache-preview]

The preview URL and token remain valid until `expiresAt`. Cache them to avoid an API round-trip on every iframe subrequest (assets, navigation, etc.).

```typescript
import { v0 } from 'v0-sdk'

type Preview = { url: string; token: string; expiresAt: string }

// Simple in-memory cache - use Redis or similar in production
const previewCache = new Map<string, Preview>()

async function getCachedPreview(chatId: string) {
  const cached = previewCache.get(chatId)
  const now = Date.now()

  // Return cached preview if still valid (with 60s buffer)
  if (cached && new Date(cached.expiresAt).getTime() - now > 60_000) {
    return cached
  }

  const { preview } = await v0.chats.getPreview({ chatId })
  if (preview) {
    previewCache.set(chatId, preview)
  }
  return preview
}
```

## 3. Proxy browser requests [#3-proxy-browser-requests]

Browsers cannot attach custom headers to an iframe navigation. Serve the preview through your own backend route, and have that route forward every path under the iframe URL to `preview.url` with the preview token.

```typescript
async function proxyPreviewRequest(
  request: Request,
  chatId: string,
  path: string[],
) {
  const preview = await getCachedPreview(chatId)

  if (!preview) {
    return new Response('Preview is not ready', { status: 202 })
  }

  const incomingUrl = new URL(request.url)
  const upstreamUrl = new URL(`/${path.join('/')}`, preview.url)
  upstreamUrl.search = incomingUrl.search

  const headers = new Headers(request.headers)
  headers.set('x-v0-preview-token', preview.token)
  headers.delete('host')

  const hasBody = request.method !== 'GET' && request.method !== 'HEAD'
  const response = await fetch(upstreamUrl, {
    method: request.method,
    headers,
    body: hasBody ? request.body : undefined,
    redirect: 'manual',
  })

  return new Response(response.body, {
    status: response.status,
    headers: response.headers,
  })
}
```

Point your iframe at your proxy URL, not directly at the returned `preview.url`. Make sure the proxy route also handles subpaths for assets and in-app requests.

```tsx
<iframe src="/api/v0-preview/chat_abc123/" />
```

## 4. Handle token expiry [#4-handle-token-expiry]

If your proxy receives an unauthorized response from the preview URL, clear the cache and retry `chats.getPreview`.

Do not send your v0 API key to the preview URL or expose it to the browser. The API key is only for calling the Platform API; preview access uses the short-lived `x-v0-preview-token` header.
