createAgent options in src/agent.ts. The starter already connects
the agent to its tools, test people and hosting environment.
These options apply to the messaging agent. An MCP server or CLI tool command does not need
a model key; the host supplies its model or invokes a tool directly.
Models
openAICompatible works with OpenAI-compatible APIs. With no explicit apiKey or baseURL,
it uses the first non-empty key in this order:
With no key, it stops immediately and names the supported variables.
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.
Use Flow’s model key
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
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.Transcribe a voice note
For a local audio file namedvoice.ogg:
Transcription
language is optional and detected when omitted.
Errors include a message, next step, code and HTTP status.
createAgent options
The tables below cover common options. The TypeScript
AgentOptions definition
is the complete contract.
Instructions and tools
Channels and identity
See messaging and people for identity and time-zone behavior, and
reminders and nudges for delivery rules.
State and hosting
Self-hosting covers persistence and restart handling.
See Deployment for Flow hosting and Traces for observability.
Turn timing and history
Durations are milliseconds. A tool’s own
deadlineMs overrides the agent’s value for that tool.
Message hooks and templates
beforeSend return values
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
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 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.