Skip to main content
When you configure an agent, FrogBot stores authenticated HTTP conversations in FrogBot collections. You can use the default chats and messages collections without adding configuration, or adopt the roles into your own collections. Anonymous agent calls remain stateless, even when a custom agent access rule allows them. Agent calls use /api/agents/<slug>. To call the embedded AI gateway directly, see the authenticated HTTP endpoints.

Use the default collections

FrogBot injects both collections when agents are configured and no collection claims either role. Chat and message collections use soft deletion. Moving a chat to the trash keeps its messages; deleting a chat permanently also deletes all of its messages, trashed ones included, whatever the messages collection’s delete access says. Chats appear under Collections in the admin panel, and Messages is hidden from the admin by default (see show messages in the admin). Access is owner-scoped through the chat’s user relationship, and a channel chat is written only from its channel. See who can write a chat. Messages store the AI SDK UIMessage.parts array. Tool calls, tool results, reasoning, and custom data parts therefore remain available on later turns instead of being flattened into text. FrogBot manages the remaining message fields and they are read-only through the API. status is queued for a message waiting behind a running turn, model and reasoning record the model and reasoning option a user message was sent with, author records who sent it, and settlements records who answered each question. A hidden frogbot-chat-turns collection tracks each chat’s current turn.

Who can write a chat

Every chat has one home, and it is written only from there. Writing covers sending a message, answering or dismissing a question, continuing a turn, and running a queued message. The owner of a channel chat can still read it, and the admin chat shows it read-only with Branch. The agent endpoints and local agent calls check this rule themselves, whatever the collections’ access says. For a channel chat, they return 409:
Local calls such as generate({ chatId }) reject with an error whose code is channel-chat and status is 409. A web chat that belongs to another user returns 404, as if it did not exist. canWriteChat from frogbot is the rule itself. It takes the request and the chat, and returns whether the request can write it:

Channel label

Chats have a virtual channelLabel field: the label of the channel piece a channel chat belongs to, such as Slack, and null for a web chat. FrogBot computes it on every read and stores nothing. It needs channel, channelKey, and channelThread from the same document, so a read that selects only channelLabel gets null.

Default collection access

A collection write these rules refuse returns 403 Forbidden, like any other access denial. These rules are defaults. Setting access on a collection marked chat: true or message: true replaces the default for each operation you set. The agent endpoints keep the rule, but REST, GraphQL, and Local API writes to the collection follow your function. Compose canWriteChat to keep channel chats and other users’ chats closed:
For update and delete, the default is the query { 'chat.user': { equals: req.user.id }, 'chat.channelKey': { exists: false } }. Include it in your own query to keep channel messages read-only.

Chat assets

Files that belong to a conversation live in a separate upload collection, frogbot-chat-assets, not in your files collection. Composer attachments and files an agent produces during a run, such as screenshots or generated documents, are stored there so a user’s own file library only contains what they chose to keep. FrogBot injects the collection whenever chat persistence is enabled and hides it from the admin panel. Each asset records an owner and, once a message references it, the chat it belongs to. The manifest exposes the slug as chat.assetsSlug. Because read access follows the chat collection’s own rule, sharing a chat also shares its attachments. A storage adapter covers frogbot-chat-assets automatically, so assets go to cloud storage without listing the collection. Set 'frogbot-chat-assets': false in the adapter’s collections to keep them on local disk. Word and Excel assets also carry a text field: the text FrogBot read from the file when it was uploaded, exactly as the model receives it after the file’s label (see Word and Excel files). Other assets have text: null. Anyone who can read the asset can read text; FrogBot ignores a value sent by the client, and no one can change it through the API. The upload response includes it, so a client can show the text without a second request. Because text can hold about 60,000 characters, leave it out of reads that don’t need it with select, such as select: { text: false }. Tools can save files against the current chat with saveChatAsset:
The helper requires chat persistence and a current chat on ctx.agent.chatId; it records the requesting user as the owner. It doesn’t read Word or Excel files when saving them, so a tool can save any file, including one that can’t be read. FrogBot reads such a file the first time a turn sends it to a model.

How attachments reach the agent

Every agent turn sends the chat’s attachments to the model again, prepared for the model that turn uses. This covers sending a message, continuing after a question, queued and steered messages, channel conversations, and the agent’s Local API generate, stream, and streamMessage. Saved messages never change: the preparation applies only to what the model receives. FrogBot sorts each attachment into one kind: The label is Attached file "notes.md":, using the file name in the message part, or the stored name when the part has none, or Pasted text: for a file-reference part with origin: 'paste' and for data-paste parts. SVG files are text. A leading byte-order mark is dropped, and bytes that aren’t valid UTF-8 become �. FrogBot sets no size limit on text. Which image, audio, video, and PDF attachments a model accepts comes from its input types. A model with unknown input types receives every image, audio, video, and PDF attachment. The chat composer applies the same rules when a file is picked, so it refuses unsupported files and files the selected model can’t read before they are uploaded; see Attachments.

Word and Excel files

FrogBot reads Word (.docx) and Excel (.xlsx) files once, when they are uploaded, and stores the result in the asset’s text field. Every model receives that text after the file’s label, including models that only read text. The original file stays in storage and is never sent to the model. A file is read as Word or Excel when its name ends in .docx or .xlsx, or, for any other name, when its media type is application/vnd.openxmlformats-officedocument.wordprocessingml.document or application/vnd.openxmlformats-officedocument.spreadsheetml.sheet. Macro-enabled .docm and .xlsm files, templates such as .dotx, older .doc and .xls files, PowerPoint, and OpenDocument files are unsupported, even when sent with the Word or Excel media type. A Word document becomes plain text that keeps its structure:
  • Headings start with one # per level, list items with - or 1., and nested items are indented.
  • Each table row is one line, such as | North | 1,200 |, with a | --- | --- | line after the first row. A merged cell’s text appears in its first cell, and the cells it covers stay empty so the columns line up.
  • Links become text (url). Footnotes and endnotes follow the body as a numbered list. Tracked insertions are kept and tracked deletions left out.
  • Images, charts, comments, headers, footers, bold, and italics are left out.
An Excel workbook becomes one CSV section per sheet, in workbook order, each starting with Sheet "<name>": and separated by a blank line. Empty sheets and chart sheets are left out; hidden sheets are included. Each cell holds its stored value:
  • A formula gives the result saved with the workbook, and is empty when the workbook was saved without results.
  • Number formats aren’t applied, so a cell shown as 25% arrives as 0.25 and $1,200.00 as 1200.
  • Dates arrive as 2026-01-15, or 2026-01-15 09:30:00 when they have a time, in UTC. A date outside the years 0 to 9999 arrives as its serial number, such as 2958466, and one too far out to convert arrives empty. Booleans arrive as TRUE and FALSE.
  • Error cells, such as #DIV/0!, are empty. A merged cell’s value appears once, in its first cell.
  • Fields containing a comma, a quote, or a line break are quoted, with quotes doubled.
Large files are cut so that one file can’t fill the model’s context on every turn:
  • Each sheet sends its first 1,000 rows, counting the header row. A longer sheet ends with a note such as [budget.xlsx: showing the first 1,000 of 52,340 rows of sheet Q1].
  • A file’s text is limited to 60,000 characters. Longer text is cut at the last line break before the limit and ends with a note such as [report.docx: showing the first 59,982 of 182,400 characters]. Notes don’t count toward the limit.
The notes are part of the stored text, so the model sees them and the composer’s text viewer shows them in the same place. A Word or Excel upload that can’t be read is refused with 400, and nothing is stored: The REST response is a validation error on the file field, naming the file:
The SDK throws a FrogBotSDKError with status 400 and this errors array, and the Local API throws the validation error. The chat UI’s REST adapter turns it into a ChatAdapterError with code file-unreadable, and the composer shows the file as a red card; see Attachment cards. An asset without stored text, such as one saved with saveChatAsset or stored before FrogBot read these files on upload, is read the first time a turn sends it, and the text is saved to the asset for later turns. If it can’t be read, the model receives the “couldn’t be read” marker below, and FrogBot tries again on the next turn.

Markers

An attachment that isn’t sent is replaced by a marker in the model’s input. Markers state facts only; the agent decides what to tell the user. A marker for a file without a name uses its kind instead, such as [Image removed: image].

Repeated files

Files are the same when their contents match. FrogBot stores a SHA-256 hash of each asset’s contents in a hidden sha256 field when the file is uploaded; a value sent by the client is ignored. An asset without a hash is matched by its ID. Files embedded directly in model messages are matched by their contents.

Size limit

When the images, PDFs, audio, and video in one model request add up to more than 25 MiB, measured as base64, FrogBot replaces the oldest of them, in message order, until 15 MiB or less remain. A single file over 15 MiB is replaced even when it is the newest. Earlier copies of repeated files, files given by URL, and text don’t count. On Amazon Bedrock, only the newest 20 images in a request are sent, and older ones get the image marker.

Missing files

When a user sends a message, an attachment that doesn’t exist or that the user can’t read fails the request with 404 or 403, and the chat stays free for the next message. On every other turn, such as a queued message or a continued turn, the attachment becomes the “couldn’t be loaded” marker and the turn goes on. Direct AI operations such as req.frogbot.generateText and the gateway endpoints send content as given; none of the above applies to them.

Use the default chat views

When an agent is configured, the chat collection create route (/collections/chats/create by default) opens an empty chat with the first configured agent. Users reach it from the Chats collection in the sidebar. Sending the first message creates the chat and changes the URL to the chat’s own page without reloading the conversation. New Chat always opens an empty chat. The chat collection list uses the standard collection list view, so filtering, sorting, and columns behave like any other collection. Selecting a chat opens its document view, restores up to 500 stored messages in chronological order, and continues the same conversation. These defaults also apply when your own collection uses chat: true. FrogBot uses that collection’s slug for create, list, and document routes.

Automatic chat titles

FrogBot titles a chat as soon as it exists. The first title is the first user message, shortened to 48 characters with ”…”. After the first assistant reply, FrogBot generates a better title with the configured utility model, replaces the shortened message, and records the call through normal usage reporting. A chat whose first message has no text, such as an image-only message, stays untitled until the generated title arrives. Naming runs in the background and never delays or fails the assistant response. If generation fails, or a hard process exit interrupts it, the chat keeps the shortened first message. Long titles fade at the edge of the chat list instead of showing an ellipsis. FrogBot only replaces a title that is empty or still equals the shortened first message. Later turns do not regenerate the title, and a title set by your application or by a user is never overwritten, including a rename made while naming runs.

Customize admin chat components

Use admin.components.chat for chat-wide component overrides. Chat replaces the canonical chat surface on chat document routes, and on the dashboard when you use chat as your home page. Greeting replaces the block shown above the composer while a chat has no messages. The role-specific action slots receive the current message, pending state, and defaultActions.
Each entry can also use a component definition with clientProps. Use the role slots for small additions or replacements. Use Chat when you need to replace the conversation shell while retaining FrogBot’s default route loading and navigation.

Greeting an empty chat

A chat with no messages shows a greeting above the composer. It renders the active agent’s avatar when the agent profile sets one, and otherwise falls back to the logo from admin.components.graphics.Logo, so a custom logo appears without extra configuration. The greeting line varies by time of day and includes the signed-in user’s name when the user record has one. Greeting receives avatar, logo, name, and userName, so an override can reuse those values or ignore them entirely.

While the agent works

From the moment a message is sent until the reply ends, a logo pulses in a row below the thread. It is the active agent’s avatar when the agent profile sets one, and otherwise the icon from admin.components.graphics.Icon, the FrogBot favicon by default. The row keeps its height when the agent is idle, so the thread doesn’t shift, and the logo stays still when the user prefers reduced motion.

Override the chat views

Set document view components under admin.components.edit.views. FrogBot only defaults the edit root view on the chat: true collection and preserves a configured override.
Override a route view when you need to replace its surrounding page behavior instead of only the chat surface. The dashboard path must remain /.

Use chat as your home page

The Admin Panel opens on the collections dashboard by default. To open a new chat at / instead, set the dashboard view to FrogBot’s chat view:
The home page then starts an empty chat with the first configured agent, and your admin.components.chat overrides apply there too. The chats collection create and document routes still open the chat screen. Pair this with a New Chat item or the Recents section from Admin navigation for a chat-first sidebar.

Customize the collections

Add chat: true or message: true to adopt a collection. FrogBot keeps your slug and merges in its required fields, hooks, access rules, and admin defaults.
Your field definitions win when they are compatible with the base schema. FrogBot appends missing base fields and combines hooks. It rejects incompatible field types and protects these fields:
  • chats.user, chats.channel, chats.externalId, chats.channelKey, chats.channelThread, and chats.channelLabel
  • messages.id
  • messages.chat
  • messages.parts
  • messages.status, messages.delivery, messages.model, messages.reasoning, messages.author, messages.settlements, and messages.version
Mark at most one collection for each role. If an unmarked collection already uses the default chats or messages slug, sanitization asks you to mark or rename it rather than adopting it implicitly.

Show messages in the admin

Messages is hidden from the admin panel by default, like FrogBot’s other internal collections. The sidebar and dashboard have no Messages entry, and the admin list and document URLs show a not-found page. The chat view, REST, GraphQL, and the Local API are unaffected. To browse raw message rows, for example while debugging a reply, adopt the collection and set admin.hidden: false:
Messages then appears directly under Collections with its default icon and list columns. An adopted message collection inherits the hidden default too, so the Turns example above stays out of the admin unless it also sets admin: { hidden: false }.

Continue a chat

The first authenticated HTTP request creates a chat and returns its ID.
JSON responses include chatId. Stream responses provide the same value in X-FrogBot-Chat-Id. Send that ID with only the next user message. FrogBot loads and validates stored history before running the agent.
FrogBot rejects a chat owned by another user. It also rejects client-supplied assistant or system messages; only the server persists assistant output.

Persist local generation

Local generation is stateless unless you provide an existing chat ID.
generate({ chatId }) persists the new user message, loads stored history, runs the agent, and stores the resulting assistant message. Local stream() calls remain stateless.

Understand durability

FrogBot awaits the user-message write before inference begins. The assistant message is saved after every model step, and again with finalized or partial parts on completion, error, abort, or client disconnect. Usage is attached when the model emits it but never blocks the message save.
FrogBot’s SQLite adapter disables transactions unless you configure transactionOptions. Without transactions, a failure between chat creation and the first message write can leave an empty chat. MongoDB and transaction-enabled adapters roll back the pair.
A hard process exit during inference loses the step in progress, and can leave a user message without an assistant reply when the first step had not finished. The chat remains valid and can be continued once its turn lease expires; resumable streams are planned separately.