Browse documentation

Examples

Support bot

Add a typed support operation or persistent ticket thread to your product.

Use a function for one question. Use an agent when a support ticket needs many messages, tools, approvals, or child investigations. Your product keeps identity, ticket rules, and the customer UI.

Define the support function

import { flary, z } from "flary";

const app = flary({ model: "openai/gpt-5" });

const searchDocs = app.fn({
  description: "Search product documentation",
  input: z.object({ query: z.string().min(1) }),
  output: z.array(
    z.object({
      title: z.string(),
      url: z.string().url(),
    }),
  ),
  run: ({ query }) => docs.search(query),
});

const tools = app.tools({ searchDocs });

export const support = app.fn({
  input: z.object({ question: z.string().min(1) }),
  output: z.object({
    answer: z.string(),
    sources: z.array(z.string().url()),
  }),
  tools,
  prompt: ({ question }) => `Answer with product facts and sources.\n\n${question}`,
});

Serve it from the generated Worker:

export default app.serve({ support });

Call it from your product

import { flary } from "flary/client";
import type { support } from "./support";

const api = flary<{ support: typeof support }>({
  baseUrl: "https://support.example.com",
  headers: () => ({ authorization: `Bearer ${sessionToken()}` }),
});

const result = await api.support({
  question: "How do I change my plan?",
});

Your application owns login, customer identity, billing, and the screen. Flary owns model execution, tools, validation, and durable runs.

Keep one thread per ticket

export const supportAgent = app.agent({
  name: "support",
  instructions: "Resolve the support issue with approved product facts.",
  tools,
});

Store the product ticket ID with thread.ref.threadId. Reopen that thread for each reply. See Build your UI or bot for the transport pattern.