Skip to main content

Server API

@flow-state-dev/server — Action runtime, stores, SSE streaming, orchestration.

Runtime

createFlowState(options)

The main entry point. Assemble a runtime from declarative config. Returns a FlowState handle. Most users want this rather than the lower-level registry and router below.

import { createFlowState, inMemoryStores } from "@flow-state-dev/server";

const flowstate = createFlowState({
flows: { myFlow },
models: { default: "openai/gpt-5.4-mini" },
stores: { default: { primary: inMemoryStores() } },
});

Selected options:

OptionTypeNotes
flowsRecord<string, FlowInstance>Required. Stable key to flow instance.
modelsFlowStateModelsConfig{ default?, intents?, ... }. Auto-wires AI Gateway via AI_GATEWAY_API_KEY.
modelResolverModelResolverEscape hatch: a pre-built resolver (test mocks, custom resolvers). Used instead of models.
voice{ provider?: VoiceProvider }A voice provider for TTS/STT, e.g. new OpenAIVoiceProvider(...). See Voice.
storesStoresConfigRequired. Named profiles of capability slots.
defaultProfilestringActive profile when FSD_ENV is unset.
settingsTSettingsRead in blocks via ctx.settings.
onError(error, ctx) => voidctx is { method, path }. HTTP-level sink.
errorCapture(event: ErrorCaptureEvent) => void | Promise<void>Opt-in, block-aware sink for routing runtime block failures to Sentry, Datadog, etc. See Error capture.
onBackgroundWork(p) => voidServerless keep-alive, e.g. (p) => after(() => p).
defaultSseHeartbeatMsnumberWire-level SSE heartbeat cadence.

Construction is synchronous and validates config (empty stores, unknown defaultProfile) by throwing FlowStateConfigError. Stores initialize lazily on first getRouter() / ready().

FlowState

The handle returned by createFlowState.

interface FlowState<TSettings extends object = FlowStateSettings> {
/** Resolve the lazy router. First call triggers async store init. */
getRouter(): Promise<FlowApiRouter>;
/** Eager warmup. Idempotent. */
ready(): Promise<void>;
/** Dispose pooled resources across every declared adapter. */
dispose(): Promise<void>;
/** The active profile name. */
readonly activeProfile: string;
/** The settings bag, typed via TSettings. */
readonly settings: TSettings;
/** Diagnostics. */
readonly meta: {
flowKeys: string[];
profileKeys: string[];
declaredSlots: Record<string, CapabilitySlot[]>;
};
}

StoreAdapter

A tagged value a store-adapter factory returns. Declares which capability slots it backs and realizes them on demand.

interface StoreAdapter {
/** Slots this adapter is willing to back. */
readonly capabilities: ReadonlyArray<CapabilitySlot>;
/** Realize the adapter for the given slots. Opens pools / runs schema init. */
resolve(slots: ReadonlyArray<CapabilitySlot>): Promise<Partial<StoreRegistry>>;
/** Release pooled resources. Called by FlowState.dispose(). */
dispose?(): Promise<void> | void;
}

CapabilitySlot is "primary" | "blobs" | "queue" | "scheduler". Only primary is implemented in Phase 1; the others are declared but forward-compatible.

Store-adapter factories: inMemoryStores(), filesystemStores({ rootDir }) (this package), postgresStores(options) (@flow-state-dev/store-postgres), sqliteStores(options) (@flow-state-dev/store-sqlite), vercelPostgresStores() (@flow-state-dev/vercel/store).

Registry & Router (lower-level)

createFlowState wraps these. Reach for them only for custom transports or advanced wiring.

createFlowRegistry()

Create a registry for flow instances.

import { createFlowRegistry } from "@flow-state-dev/server";

const registry = createFlowRegistry();
registry.register(myFlow);

createFlowApiRouter(options)

Create HTTP route handlers for the flow API from a registry.

import { createFlowApiRouter } from "@flow-state-dev/server";

const router = createFlowApiRouter({
registry,
stores, // a resolved StoreRegistry
modelResolver: createModelResolver(), // optional
voiceProvider: new OpenAIVoiceProvider({ apiKey: process.env.OPENAI_API_KEY }), // optional, for TTS/STT
});

export const { GET, POST, PATCH, DELETE } = router;

parseFlowRoute(path)

Parse a flow API path into its components (kind, action, sessionId, etc.).

Execution

createExecutionContext(options)

Create a block execution context manually (for advanced use).

runAction(options)

Execute a flow action programmatically.

executeBlock(block, input, ctx)

Execute a block directly with a given context.

Stores

createFilesystemStores(options?)

Filesystem-backed persistence (default).

import { createFilesystemStores } from "@flow-state-dev/server";

const stores = createFilesystemStores({ basePath: ".flow-state" });

createInMemoryStores()

In-memory persistence for testing.

import { createInMemoryStores } from "@flow-state-dev/server";

const stores = createInMemoryStores();

Model Resolution

createModelResolver(options?)

Create a model resolver. Auto-detects providers from environment variables with zero config, or accepts explicit keys, presets, and retry policy.

import { createModelResolver } from "@flow-state-dev/core/models";

// Zero-config: auto-detects from env vars
const resolver = createModelResolver();

// With options:
const resolver = createModelResolver({
keys: { openai: "sk-..." },
presets: { fast: { models: ["openai/gpt-5.4-mini"] } },
retryPolicy: { maxAttemptsPerModel: 3 },
});

Model strings use slash format: "openai/gpt-5.4", "anthropic/claude-sonnet-4-6", "vercel/openai/gpt-5.4".

parseModelString(modelString)

Parse a slash-format model string into its components (provider, model, gateway).

Voice

Voice surfaces are owned by a VoiceProvider passed as voiceProvider to createFlowApiRouter (or voice: { provider } on createFlowState). A provider declares its abilities and implements speak / speakStream / transcribe / listVoices. Concrete providers ship in their own packages (e.g. @flow-state-dev/voice-openai). See Voice for the full guide.

createCompositeVoiceProvider(config)

Combine providers per ability (e.g. synthesize with one, transcribe with another). Exported from @flow-state-dev/core.

createSentenceBuffer()

Create a sentence-boundary detection buffer for TTS text chunking.

createTTSPipeline(options)

Create the synthesis pipeline that converts text deltas into OutputAudioContent.

createTTSEmitterHook(options)

Create an event observer that wires the TTS pipeline to a ResponseEmitter.

Streaming

createResponseEmitter(options)

Create a stream emitter for manual event emission.

encodeStreamEvent(event)

Encode an event for SSE transmission.

serializeSSEFrame(event)

Serialize an event into SSE wire format.

replayRequestEvents(options)

Replay stored events from a completed request (for resume).

Errors

FlowError

Base error class with code, retryable, blockName, scope, and cause.

Subclasses:

  • ValidationError — Schema validation failure (not retryable)
  • NetworkError — Network issues (retryable)
  • TimeoutError — Operation timeout (retryable)
  • RateLimitError — Rate limit hit (retryable)
  • ModelError — Model provider error (retryable)
  • ToolExecutionError — Tool block failure (varies)

normalizeError(error)

Convert any thrown value to a FlowError.