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#

npm install @mynthio/sdk

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

// 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 (payload, { request }) => {
          console.log(payload.task.id, payload.result.images, request.url);
        },
        imageTaskFailed: async (payload) => {
          console.error(payload.task.id, payload.errors);
        },
      }),
    },
  },
});

The handler verifies X-Mynth-Signature against the raw body, requires X-Mynth-Event to match the body's event and X-Mynth-Delivery to be present, and rejects a timestamp more than five minutes off, all with 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 the payload and the route context: request, params, and middleware context, plus deliveryId, the X-Mynth-Delivery value. The handler names are the same as in every SDK helper. See the table on webhooks. Unsigned per-request webhook.custom deliveries are rejected.

Generate on the server#

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

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",
});

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

generate() waits for the task. generateAsync() returns the id and a browser token right away, for when the caller cannot wait. Tasks and polling covers both.

TanStack AI#

@mynthio/tanstack-ai-adapter is for apps that already call generateImage() from @tanstack/ai. It depends on @mynthio/sdk and needs @tanstack/ai 0.34.0 or newer as a peer. It does not verify webhooks. The route above still does that.

npm install @mynthio/tanstack-ai-adapter @tanstack/ai
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, 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.

The adapter calls the SDK's generate() and returns the finished images. tanstack-start-ai-mynth-adapter is a complete Start app that streams the TanStack call.

Next steps#