# TanStack Start

> Receive signed Mynth webhooks in a TanStack Start server route, generate from a server function, and use the TanStack AI image adapter.

`@mynthio/sdk/tanstack-start` exports `mynthWebhookHandler` for a Start server
route. Generation from a server function uses the normal SDK. If the app
already uses TanStack AI's `generateImage()`, the separate
`@mynthio/tanstack-ai-adapter` package plugs Mynth into it.

## Receive the webhook

```bash
npm install @mynthio/sdk
```

Put `MYNTH_WEBHOOK_SECRET`, the `whsec_` secret from registering the endpoint,
in the server environment.

```ts
// src/routes/api/mynth-webhook.ts
import { createFileRoute } from "@tanstack/react-router";
import { mynthWebhookHandler } from "@mynthio/sdk/tanstack-start";

export const Route = createFileRoute("/api/mynth-webhook")({
  server: {
    handlers: {
      POST: mynthWebhookHandler({
        imageTaskCompleted: async (result, { request }) => {
          console.log(result.taskId, result.images, request.url);
        },
        imageTaskFailed: async (result) => {
          console.error(result.taskId, result.errors);
        },
      }),
    },
  },
});
```

The handler verifies the Standard Webhooks `webhook-signature` against
`webhook-id`, `webhook-timestamp` and the raw body, and requires the body's
`id` to be the `webhook-id`. A missing or wrong signature, or a timestamp more
than five minutes off, answers `400`. An event with no handler, or one it does
not know, answers `200`. It reads the secret on each request, from
`MYNTH_WEBHOOK_SECRET` or from `{ webhookSecret }` as the second argument, and
throws if neither is set.

It reads a clone of the request, so the route's `request` stays unread. Each
callback receives a mapped result or task failure and the route context: `request`, `params`, and
middleware `context`, plus the verified `event`. `event.id` is the
`webhook-id`, the same on every retry, so store it to skip events you have
already handled. The handler names are the same as in every SDK helper. See
the [table on webhooks](https://mynth.io/docs/concepts/webhooks.md#receive-events).
Unsigned per-request `webhook.custom` deliveries are rejected unless you pass
`{ unsigned: { verify } }`. See
[per-request webhooks](https://mynth.io/docs/concepts/webhooks.md#per-request-webhooks).

## Generate on the server

Keep `MYNTH_API_KEY` on the server and call the SDK from a server function:

```ts
import Mynth from "@mynthio/sdk";

const mynth = new Mynth();

const pending = await mynth.image.generateAsync({
  model: "black-forest-labs/flux.2-pro",
  prompt: "A lighthouse at dusk, film grain",
  generate_public_access_token: true,
});

return { taskId: pending.id, token: pending.access.publicAccessToken };
```

`generate()` waits for the task. `generateAsync()` returns the id right away,
plus a browser token when the request sets `generate_public_access_token`,
for when the caller cannot wait.
[Tasks and polling](https://mynth.io/docs/sdks/typescript/tasks.md) covers both.

## TanStack AI

`@mynthio/tanstack-ai-adapter` is for apps that already call `generateImage()`
from `@tanstack/ai`. It calls the Mynth API itself, so it needs no other
package: only `@tanstack/ai` 0.61.0 or newer as a peer. It does not verify
webhooks. The route above still does that.

```bash
npm install @mynthio/tanstack-ai-adapter @tanstack/ai
```

```ts
import { generateImage } from "@tanstack/ai";
import { mynthImage } from "@mynthio/tanstack-ai-adapter";

const result = await generateImage({
  adapter: mynthImage("black-forest-labs/flux.2-pro"),
  prompt: "A lighthouse at dusk, film grain",
});

console.log(result.images[0]?.url);
```

- `mynthImage(model, config?)` binds one model. The key comes from
  `MYNTH_API_KEY` or `config.apiKey`.
- `createMynthImage({ apiKey, baseURL, defaultHeaders, destination })` returns a
  function that takes a model, for several adapters sharing one config.
- Image content parts become Mynth `inputs` on models listed in
  `MYNTH_IMAGE_INPUT_MODELS`. Other Mynth fields go in `modelOptions`.
- `MYNTH_IMAGE_MODELS` is the id list built into the package version, and it
  includes `auto`. Pass an explicit id. The live list is
  `GET https://api.mynth.io/models`.
- Failures are `MynthError` subclasses with a `code` that TanStack AI reports on
  `RUN_ERROR`: the API's own code (such as `insufficient_balance`) for
  `MynthAPIError`, or `MYNTH_TASK_FAILED`, `MYNTH_NO_IMAGES` and
  `MYNTH_TASK_TIMEOUT`.

The adapter creates the task, polls it until it settles, and returns the
finished images.
[tanstack-start-ai-mynth-adapter](https://github.com/mynthio/oss/tree/main/examples/tanstack-start-ai-mynth-adapter)
is a complete Start app that streams the TanStack call.

## Next steps

- [Webhooks](https://mynth.io/docs/concepts/webhooks.md): registering the endpoint, events, sources, and retries.
- [Generate images](https://mynth.io/docs/guides/generate-images.md): the request fields.
