Skip to content

Ask the docs

Answers come only from these docs and are written by an AI model (Gemini), so check the linked pages. Your question is not kept. Do not include personal details or keys.

Choose a model and configure your messaging agent's runtime behavior.

Configure the model and createAgent options in src/agent.ts. The starter already connects the agent to its tools, test people and hosting environment.

openAICompatible works with OpenAI-compatible APIs. With no explicit apiKey or baseURL, it uses the first non-empty key in this order:

OrderEnvironment variableProvider
1OPENROUTER_API_KEYOpenRouter
2OPENAI_API_KEYOpenAI
3ANTHROPIC_API_KEYAnthropic
4GEMINI_API_KEYGemini
5FLOW_KEYFlow’s hosted models

With no key, it stops immediately and names the supported variables.

import { openAICompatible } from "@flow-engineer/flow";
const model = openAICompatible({ model: process.env.MODEL });

Use your local server’s address and model id in the second example. A server that needs a key can also receive apiKey explicitly.

MODEL sets the default model id. The starter passes CAFE_MODEL when set. Otherwise each provider has a default in src/model.ts. Set contextTokens to tell Flow the model’s context-window size.

flow setup writes FLOW_KEY, which can use the account’s $2 of trial model credit. A provider key takes priority over it. flow status reports the remaining credit without printing the key.

flowModel({ model }) explicitly selects Flow’s hosted models; its default is anthropic/claude-haiku-5.5. GET /models on the configured Flow model server lists the models it offers. Set FLOW_MODEL_URL to use another Flow model server.

Rotation and exhausted trial credit

flow setup --rotate key replaces FLOW_KEY. The old key works for another 24 hours; --now ends it immediately. Neither command prints the full key.

Exhausted credit returns trial_credit_used with a next step. Add your own provider key to continue with that provider.

For a local audio file named voice.ogg:

Transcription
import { readFile } from "node:fs/promises";
import { flowTranscribe } from "@flow-engineer/flow";
const audio = await readFile("voice.ogg");
const { text } = await flowTranscribe(audio, {
filename: "voice.ogg",
mimeType: "audio/ogg",
});
console.log(text);

This uses the same Flow key and model-server address. It accepts a Blob, File, Buffer, Uint8Array or ArrayBuffer, up to 15 MB. language is optional and detected when omitted. Errors include a message, next step, code and HTTP status.

The tables below cover common options. The TypeScript AgentOptions definition is the complete contract.

OptionPurpose
dirFolder containing AGENTS.md and skills/
filesSupply those files as text when there is no folder
modelA Model, usually from openAICompatible
toolsYour tool definitions
inlineSkillsInclude every skill in the prompt rather than loading on demand
OptionPurpose
channelswhatsapp(), sms(), customUI() or provider channel definitions
resolveResolve a channel identity to your account key
testPeoplePeople the playground may play, as [{ name, phone }]
profileAccount data fetched on demand through get_user_profile
contextSmall facts added before every turn; keep slow lookups out of it
timeZoneThe business time zone, used when the person’s is unknown
quietHoursHours to hold business-started messages; false disables them
transcribeSpeech-to-text for incoming voice notes

See messaging and people for identity and time-zone behavior, and reminders and nudges for delivery rules.

OptionPurpose
databaseDurable storage; defaults to an in-memory SQLite database
storeSupply your own store instead of a database
recordsCollections of the agent’s own small records, kept in the same store (records)
wakeUpsfalse removes the model’s wake_me_up and cancel_wake_up tools and their list from its prompt; wake-ups set by code still run (wake-ups)
hostHosting options passed as host: options.host from buildAgent
routesYour HTTP handlers, keyed by path
eventsEvent sinks receiving runtime events
tracesTrace export settings; defaults come from the environment

Self-hosting covers persistence and restart handling. See Deployment for Flow hosting and Traces for observability.

OptionDefaultPurpose
debounceMs300Quiet time after a message before a reply starts
deadlineMs60,000How long a reply waits for a tool before moving it to the background
partialAfterMs5,000How long a finished tool result waits for other tools in that response
maxSteps8Model steps per turn
history.maxTokens60% of contextTokens, else 50,000History carried into each model call
history.toolResultChars12,000Limit for a tool result in history

Durations are milliseconds. A tool’s own deadlineMs overrides the agent’s value for that tool.

OptionPurpose
beforeSendChange or refuse the final message before validation, sending and recording
interceptAnswer an incoming message without a model call
templatesYour declared WhatsApp templates
fallbackTemplateThe template used when a nudge meets a closed WhatsApp window
shareUpdatesEnable, disable or customize live status guidance in web and app interfaces
beforeSend return values

beforeSend(message, { channel, address, info, origin }) runs for messages, carousels and templates on every channel. Return nothing to send the input, another message to replace it, or { refuse: "why" } to tell the agent to try again.

It runs before platform content checks. History, transcripts and test judges see what was actually sent.

An edit’s new text runs through it too, as a message with only text, and context.edit.messageId names the message being edited. A replacement is what goes out; a refusal sends nothing.

Edits, unsends and message ids

On channels whose info has edits and unsends (Telegram and iMessage on Flow Messaging, or a custom UI with editMessage and unsendMessage), the agent is offered edit_message ({ message_id, text }) and unsend_message ({ message_id }). Its send results there name each message ({"delivered":true,"channel":"imessage","message_id":"msg_..."}). WhatsApp and SMS offer neither tool.

The platform decides how late an edit or unsend may come. Flow Messaging refuses a late one when it is made (422 unsupported_content with edit_window_closed, unsend_window_closed, first_message_not_editable or not_on_sms), and the agent gets that refusal as its tool result, for example Not edited: unsupported_content (edit_window_closed, on imessage): iMessage allows edits within 15 minutes of sending; .... Nothing is sent instead.

Your own code gets the id too: a send’s SendReceipt.messageId, the messageId on message.out events, and a sent nudge’s NudgeResult.messageId. message.edited and message.unsent events record each change. Requires @flow-engineer/flow 0.7.0 or later.

intercept and keyword messages

intercept(conversationId, text, message) sees the incoming channel and any STOP, START or HELP classification in message.keyword.

Its answer is stored in history and the transcript. STOP and START are enforced even if the hook returns an answer or throws. See keyword handling.