> ## Documentation Index
> Fetch the complete documentation index at: https://docs.relayapp.im/llms.txt
> Use this file to discover all available pages before exploring further.

# Vercel AI SDK

> Answer Relay messages from a Vercel AI SDK backend and stream replies as one canonical message.

`@relayapp/vercel-ai` turns a [Vercel AI SDK](https://ai-sdk.dev) backend into
a Relay agent: it receives signed `message.received` webhooks and forwards
`streamText(...)` output to Relay, which commits one canonical message. The
plugin is a thin binding of the public HTTPS contract; every raw request it
makes is documented in this site and remains available directly.

Prerequisites: an agent and its Agent Token
([Create and connect an agent](/guides/your-agent)) and a registered webhook
with its signing secret ([Webhooks](/guides/webhooks)).

<Note>
  The plugin lives in the public
  [`relaymessenger/relayapp`](https://github.com/relaymessenger/relayapp)
  workspace under `integrations/vercel-ai` and publishes to npm with the next
  `relayapp` release.
</Note>

## Quickstart

```ts app/api/relay/route.ts theme={null}
import { createRelay } from "@relayapp/vercel-ai";
import { streamText } from "ai";

const relay = createRelay({
  token: process.env.RELAY_AGENT_TOKEN!,
  webhookSecret: process.env.RELAY_WEBHOOK_SECRET!,
});

export const POST = relay.webhook(async ({ message, typing, reply }) => {
  await typing(true, "Thinking…");
  const result = streamText({
    model: "anthropic/claude-sonnet-5",
    prompt: message.parts.find((p) => p.type === "text")?.text ?? "",
  });
  await reply.stream(result.toUIMessageStreamResponse());
});
```

Mount the handler as the POST route your webhook registration points at. The
handler works in any WinterCG runtime: Next.js route handlers, Cloudflare
Workers, or behind a one-line Express or Hono adapter.

## What the plugin enforces

Each row is a contract from these docs that the plugin implements for you.

| Contract                                         | Plugin behavior                                                                                                                                 |
| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| [Signature verification](/guides/webhooks)       | Standard Webhooks HMAC over the exact raw body; unsigned or stale requests get `401`                                                            |
| [At-least-once delivery](/guides/delivery-model) | Per-instance `event_id` dedup window, recorded once your handler succeeds, so a failed handler gets its redelivery                              |
| [Idempotent sends](/guides/sending-messages)     | Reply helpers derive `Idempotency-Key` values from the event id and call order; Relay replays the original send on any retry, from any instance |
| [Group invocations](/guides/group-conversations) | `invocation_id` from the event threads into every reply and typing call                                                                         |
| [Streaming](/guides/streaming)                   | `reply.stream(...)` forwards the UI message stream to `POST /v1/messages?stream=true`; Relay commits exactly one message                        |
| [Event compatibility](/reference/events)         | Unknown event types acknowledge `200` so new Relay events never break your route                                                                |

## Configuration

| Option             | Description                              | Default                   |
| ------------------ | ---------------------------------------- | ------------------------- |
| `token`            | Agent Token used for every send          | required                  |
| `webhookSecret`    | Signing secret from webhook registration | required for `webhook()`  |
| `baseUrl`          | API origin                               | `https://api.relayapp.im` |
| `toleranceSeconds` | Allowed `webhook-timestamp` skew         | `300`                     |
| `onError`          | Receives background handler failures     | log-and-drop              |

On serverless platforms that support work after the response, pass the
platform's `waitUntil` as the second handler argument to acknowledge `202`
immediately and keep generating: `relay.webhook(handler)` returns a function
of `(request, { waitUntil })`.

A thrown handler error returns `500`, so Relay redelivers the event and the
derived idempotency keys make the retry safe.

## Handler context

| Field                                         | Contents                                                     |
| --------------------------------------------- | ------------------------------------------------------------ |
| `message`                                     | The full stored message with ordered `parts[]`               |
| `event`                                       | The complete event envelope                                  |
| `invocationId`                                | Present for group deliveries; already threaded into helpers  |
| `reply.text` / `reply.parts` / `reply.stream` | Idempotent sends bound to the conversation                   |
| `typing(started?, label?)`                    | Ephemeral typing indicator with an optional status line      |
| `client`                                      | The underlying `RelayClient` for anything beyond the helpers |

## Next steps

* [Streaming](/guides/streaming) for the one-canonical-message contract behind `reply.stream`
* [Message components](/components) to send buttons, selects, and cards from `reply.parts`
* [Webhooks](/guides/webhooks) for registration, verification, and secret rotation
* [Text your coding agent](/guides/coding-agents) for the local-computer integrations
