- Published on
Chat-Based Automation for Complex SaaS Workflows

How we layered conversation over a 44-field theme editor without rebuilding the UI
We wanted to test how far chat-based automation could go inside a real SaaS product. So we chose one of our own more complex interfaces: the chatbot theme editor in Predictable Dialogs.
In the demo, one chatbot acts as the theming assistant while the other is the chatbot being configured. You can ask the assistant to change colors, gradients, shadows, avatars, or prompt buttons and immediately see those changes applied to the second chatbot. That is why the demo shows two chatbots side by side.
Here is what the theming assistant configuration looks like inside Predictable Dialogs: the workflow-specific instructions plus the three tools it uses to fetch the current theme, retrieve Custom CSS documentation, and return the updated theme to the browser.

The fact that one chatbot configures another is specific to our product. The underlying pattern is far broader. The same approach can power filling complex forms, registering users, configuring accounts, onboarding customers, changing application settings, or almost any workflow that is currently driven through a UI.
Instead of forcing users to locate the right screen, field, dropdown, or button, you give them another way to operate the application: conversation.
For this experiment we deliberately selected a relatively complex workflow. The Predictable Dialogs theme editor contains approximately 44 structured inputs, plus Custom CSS that can style dozens of individual elements.
The question we set out to answer was straightforward:
Could we place a chat-based automation layer over this existing editor without rebuilding the editor itself?
You can try the conversational theming example here: View the live demo
The public demo shows one chatbot configuring another, but it does not include the final Save flow. To experience the full version—where changes are applied to the editor and you can save or discard them—create an agent in Predictable Dialogs: Create an agent
TL;DR – Key principles for chat-based automation
- Fetch real state. If your application already knows something, retrieve it. Do not ask the model to infer it from conversation.
- Pass known context. Do not force users to supply information the application already has (
agentId,customerId,workspaceId, etc.). - Prefer structured settings. Use existing schema fields whenever possible. Treat Custom CSS, or equivalent free-form output, as the fallback, not the default.
- Keep the existing app in control. Let the AI update application state for preview; leave validation, permissions, and persistence with the original UI and APIs.
- Chat is an additional interface. You do not need to rebuild your product as an “AI app.” Connect the model to your current APIs, state, and knowledge sources.
The starting point: a fairly complex manual editor
Our manual theme editor already gives users control over most aspects of chatbot appearance: fonts, background colors, assistant and user bubble colors, border roundness, avatars, input styling, button styling, welcome content, starter prompts, and more.
Then there is customCss.
Custom CSS handles the capabilities the structured editor does not expose directly—gradients, shadows, borders, advanced hover states, precise spacing, and styling of individual widget parts.
We had no intention of replacing the manual editor. We simply wanted a more powerful second mode of operation:
“Make the user messages blue with white text.”
or
“Add a subtle gradient and shadow to the prompt buttons.”
The application should then determine which settings need to change.
Step 1: Choose the workflow you want to automate
The first step is not adding an LLM. It is identifying which part of your existing application is worth automating.
We considered the AI configuration screen, but the theme editor proved a stronger test case because of its large number of inputs and multiple styling mechanisms.
Strong candidates are workflows in which users currently must understand the internal structure of your product before they can achieve a goal—knowing which settings page holds a particular option, which form fields are required, the correct order of steps, or which configuration values work together.
A conversational layer can shift some of that complexity from the user to the agent.
Step 2: Create an agent for the workflow
Inside Predictable Dialogs we created a dedicated agent for theme configuration. Under Knowledge & actions we added three capabilities:
- An HTTP Tool to fetch the existing theme
- A Knowledge tool containing our Custom CSS documentation
- A Website Payload tool to return the updated theme to the browser
These three tools form the core of the automation. The flow looks roughly like this:
User
|
| "Add a gradient to the prompt buttons"
v
Theming Agent
|
+----> Get Theme
| |
| +----> Existing application API
|
+----> Custom CSS Docs
| |
| +----> Retrieve relevant CSS documentation
|
+----> Set Theme
|
+----> Structured JSON
|
v
Browser
|
v
Theme preview
Importantly, the model never manipulates the page directly. It interacts exclusively through tools that connect it to the existing application.
Step 3: Fetch the application’s existing state
Suppose the user says:
Make the assistant bubbles darker.
That instruction only makes sense in the context of the theme the user already has. The first tool we built was therefore Get Theme.
This uses the Predictable Dialogs HTTP Tool to call our existing backend and retrieve the current theme.
The HTTP Tool supports GET, POST, PUT, PATCH, and DELETE endpoints. In this case it performs a simple GET.
This establishes a core rule for chat-based automation:
If your application already knows something, fetch the real state instead of asking the model to infer it from conversation.
The backend remains the source of truth.
Step 4: Define the API inputs
The HTTP tool requires a schema for the API it calls. For a small endpoint you can define inputs field by field. With approximately 44 fields that approach was impractical.
Instead we used Paste Example JSON. We pasted a representative theme object; Predictable Dialogs generated an initial schema. We then refined types, enums, and any fields that could not be inferred accurately.
This pattern is especially useful when automating an existing application whose APIs already accept structured objects. You rarely need to describe every field manually to the model.
Step 5: Authenticate as the logged-in user
Fetching the current theme introduced an authentication requirement: the endpoint must know which user is making the request.
We did not want a generic API credential stored inside the agent. The tool should call our backend as the actual person currently using the application. We solved this with pass-through authentication.
Our application obtains a short-lived JWT for the logged-in user and passes it through the chat widget. The HTTP tool then includes that token when calling the backend. The token never becomes a permanent credential inside the agent.
This approach is particularly valuable when chat automation lives inside an authenticated SaaS application: the agent reuses your existing authorization model rather than inventing a second one.
Step 6: Give the agent application context
Authentication answers “Who is the user?” Our API also needs to know “Which agent is the user currently editing?”
The theme endpoint expects an agentId path parameter. The browser already knows the current agentId; asking the user to supply it would be unnecessary friction. We therefore pass it via contextVariables.
contextVariables: {
agentId: currentAgentId
}
The model can then use that value when constructing the path parameter for the HTTP tool. The same pattern applies to identifiers such as:
customerId
workspaceId
projectId
productId
organizationId
pageId
The principle is simple: Do not make users tell the AI information the application already knows.
Step 7: Prefer structured settings whenever possible
Once the current theme is available, the agent must decide how to implement the request. Many common changes map cleanly to structured properties. For example:
Make the user messages blue with white text.
can be expressed as:
theme.chat.guestBubbles.backgroundColor
theme.chat.guestBubbles.color
There is no reason to generate CSS for this. We instruct the agent to prefer structured settings whenever the requested change can be represented by them.
Structured settings are easier to validate, easier to reason about, and less likely to conflict with other parts of the UI. Custom CSS remains the fallback, not the default.
Step 8: Retrieve specialized knowledge when necessary
Consider a different request:
Give the assistant bubble a subtle gradient and shadow.
Our structured theme schema has no properties for gradients or shadows, so Custom CSS is appropriate. The model must still know the exact CSS selectors supported by our widget; inventing class names is unacceptable.
We therefore created a Knowledge tool called Custom CSS Docs. It contains documentation for selectors such as:
.agent-host-bubble
.agent-guest-bubble
.agent-button
.agent-input
.initial-prompt-button
.agent-message-action-button
and many others.
When a request requires advanced styling, the agent searches this knowledge source for the correct selectors before generating CSS. Retrieval is treated as the authoritative source of valid selectors. If documentation has not been retrieved, the agent must not invent a selector.
This pattern extends beyond CSS. Detailed or frequently changing domain knowledge does not need to live permanently in the system prompt; it can reside in a knowledge source and be retrieved only when needed.
Step 9: Return structured data to the browser
At this point the agent knows:
- what the user requested,
- the current theme state,
- which structured settings are available,
- which Custom CSS selectors are valid when advanced styling is required.
The remaining step is to send the result back to the application. We created a Website Payload tool named Set Theme.
A Website Payload tool is similar to an HTTP action in that it has a name, description, and schema. The critical difference is the destination of the generated output: an HTTP tool sends data to an endpoint; a Website Payload tool sends the structured data back to the website running the chatbot.
When the model produces something like:
{
"theme": {
"chat": {
"guestBubbles": {
"backgroundColor": "#1e40af",
"color": "#ffffff"
}
}
}
}
our frontend receives that payload through the widget’s tool-result callback.
The frontend recognizes the result as coming from Set Theme and applies the returned JSON to the existing editor state. The chatbot preview updates immediately.
Step 10: Keep the existing application in control
This is one of the most important design decisions.
The AI does not persist the theme. It only mutates the state of our existing editor. Users immediately see the result of the AI’s work while the familiar Save and Discard controls remain fully operational.

In the screenshot the chatbot on the left is the theming assistant; the chatbot on the right is the one being styled. After the assistant generates an update, the normal Save control becomes active. If the user likes the result they save it. If not, they can discard the change or ask for another revision:
Make the gradient more subtle.
The assistant can call Set Theme again and refresh the preview. Calling Set Theme itself never persists configuration; persistence remains the responsibility of the existing application.
This creates a clean separation of concerns:
AI decides what should change
↓
Existing UI previews the change
↓
User reviews it
↓
Existing application persists it
You do not have to grant an agent unrestricted access to every mutation endpoint. Often the better approach is to let the AI operate the existing application state while the application retains responsibility for validation and persistence.
How the three tools work together
Consider the request:
Add a gradient to the prompt buttons.
The agent first calls Get Theme to understand the current configuration. It determines that a gradient cannot be expressed through structured fields, so it searches Custom CSS Docs for the relevant prompt-button selector. It then preserves the user’s existing Custom CSS, adds only the new styling required, and avoids removing unrelated rules. Finally it calls Set Theme, which delivers a structured payload to the browser. The editor is hydrated with the new state and the user sees the updated chatbot immediately.
From the user’s perspective they simply typed a sentence. Behind that sentence the agent fetched application state, used authenticated APIs, consulted domain documentation, generated structured data, and updated the UI.
The instructions we gave the theming agent
Tools provide capabilities; the agent still needs clear instructions on how to use them. These are the instructions we ship:
You configure the visual appearance of a Predictable Dialogs chatbot.
You cannot help in anything else.
Communicate in an empathetic and supportive manner.
Provide responses with a balanced level of detail.
A theme contains a customCss field along with many other structured
theme fields. Use the structured theme fields by default.
While setting a structured field, always check the customCss so that
it does not have any conflicting changes. If there are conflicting
changes, also update the custom css to remove the conflicting changes
so the structured theme field works, as custom css overrides the
structured fields.
The structured fields available in this schema, excluding
theme.customCss, are:
* theme.chat.inputs.color
* theme.chat.inputs.backgroundColor
* theme.chat.inputs.placeholderColor
* theme.chat.buttons.color
* theme.chat.buttons.backgroundColor
* theme.chat.roundness
* theme.chat.hostAvatar.url
* theme.chat.hostAvatar.isEnabled
* theme.chat.guestAvatar.url
* theme.chat.guestAvatar.isEnabled
* theme.chat.hostBubbles.color
* theme.chat.hostBubbles.backgroundColor
* theme.chat.hostBubbles.isCorrectivePopupEnabled
* theme.chat.hostBubbles.isMessageActionBarEnabled
* theme.chat.guestBubbles.color
* theme.chat.guestBubbles.backgroundColor
* theme.general.font
* theme.general.background.type
* theme.general.background.content
* welcome.icon
* welcome.title
* welcome.iconUrl
* welcome.subtitle
* inputOptions.type
* inputOptions.buttonText
* inputOptions.placeholder
* inputOptions.buttonIconUrl
* initialPrompts.icon
* initialPrompts.text
* initialPrompts.iconUrl
And separately, the fallback field is:
theme.customCss
The custom css classes are as below. Prefer to use a class name to
search the docs.
.agent-avatar-container — Assistant avatar column
.agent-button — Primary buttons
.agent-chat-view — Message viewport
.agent-embed-container — Widget root
.agent-feedback-popup — Feedback popup
.agent-feedback-popup-actions — Popup button row
.agent-feedback-popup-button — Popup buttons
.agent-feedback-popup-input — Popup textarea
.agent-feedback-popup-label — Popup label
.agent-guest-bubble — User bubble
.agent-host-bubble — Assistant bubble shared
.agent-host-bubble-content — Assistant text layer
.agent-host-bubble-wrapper — Assistant bubble wrapper
.agent-input — Input surfaces
.agent-input-container — Input panel width
.agent-message-action-bar — Message action row
.agent-message-action-button — Action icon button
.agent-message-action-icon-fill — Filled icon layer
.agent-message-action-icon-outline — Outline icon layer
.ai-bubble — Assistant markdown
.bubble-typing — Typing bubble
.bubble1 — Typing dot one
.bubble2 — Typing dot two
.bubble3 — Typing dot three
.chat-container — Message stream
.clear-button, #clear-button — Clear / Reset chat button
.fixed-input-overlay — Fixed input surface
.guest-container — User message row
.initial-prompt-button — Starter prompt card
.initial-prompt-icon — Prompt icon wrapper
.initial-prompt-text — Prompt text
.initial-prompts-heading — Welcome heading stack
.initial-prompts-list — Prompt list
.initial-prompts-panel — Welcome prompts panel
.initial-prompts-subtitle — Welcome subtitle
.initial-prompts-title — Welcome title row
.initial-prompts-title-icon — Welcome icon wrapper
.scrollable-container — Scrollable viewport
.selected — Selected action state
.send-icon — Send SVG icon
.send-icon-image — Send image icon
.text-input — Text input fields
## General approach
When the user asks to change the appearance of the chatbot:
1. Use "Get theme" to fetch the existing theme, so you can understand
the user request in the context of the existing theme.
2. Determine whether the requested change can be made using the
existing structured theme properties and whether there are any
conflicting custom css entries.
If the request can be completed using the structured theme properties,
set those properties and also remove conflicting styles from customCss.
3. When the requested design requires styling beyond what the normal
theme properties support, use Custom CSS. Use "custom css docs" with
a query to search what is available.
4. When both structured settings and Custom CSS are needed, use
structured settings for the parts they support and Custom CSS only
for the remaining advanced styling.
IMPORTANT: Add the custom css to the existing custom css so previous
settings are not lost. Unless previous settings conflict with the
current request, retain them.
For example, if the user says:
"Make the user messages blue with white text."
Use:
theme.chat.guestBubbles.backgroundColor
theme.chat.guestBubbles.color
Do not write Custom CSS for this.
Do not put gradients into structured fields that expect a normal color
value.
For a gradient, search the custom css docs to find the appropriate
class or selector and generate custom css targeting that selector.
## When Custom CSS is appropriate
Use Custom CSS when the requested visual change cannot be represented
by the structured theme fields.
Do not make up CSS classes.
Examples include gradients, borders, box shadows, advanced hover or
focus states, precise spacing, custom sizing, Markdown typography,
code block styling, scrollbar styling, avatar effects, advanced
starter-prompt styling, message action buttons, typing indicators,
responsive styling, and widget components without a structured field.
## Custom CSS retrieval
Use "custom css docs" to retrieve Custom CSS documentation using a
query based on the user's request.
If there are no results, try changing the query a maximum of two times.
Treat the retrieval as the authoritative reference for widget selectors.
Do not assume undocumented selectors.
When no Custom CSS documentation has been retrieved, prefer structured
theme settings.
## Updating the theme
Use Set Theme to update the theme.
The values supplied to the tool are used to set the user's theme in
the browser.
Calling Set Theme does not persist the changes.
The user can see the result and then save it if they like it. If not,
they can request another change and Set Theme can be called again.
The user can also continue using the Manual tab for simple changes.
The instructions deliberately spell out both the structured fields and the precise conditions under which Custom CSS should be used, because one of the agent’s primary responsibilities is deciding which mechanism is appropriate for each change.
We have tested this configuration with gpt-4.1-mini and gpt-5.4-mini.
The broader pattern
The interesting outcome of this experiment is not chatbot theming itself. The theme editor was simply the workflow we chose to test.
The reusable architecture is:
Natural-language request
↓
Application context
↓
Fetch existing state
↓
Retrieve domain knowledge when required
↓
Generate a structured action
↓
Apply it to the existing application
In our implementation those pieces became:
contextVariables
+
HTTP Tool
+
Knowledge
+
Website Payload
Once they are connected, the model functions as an orchestration layer over an application that already exists.
You do not need to rebuild your product as an “AI app.”
Your existing forms can stay.
Your existing APIs can stay.
Your existing database, authentication, validation, permissions, and UI can stay.
Chat simply becomes another interface for operating them.
That is the most useful way to think about chat-based automation. It is not about replacing every button and form with a chatbot. It is about letting users express what they want to accomplish while the agent determines which parts of the existing application must be used to accomplish it.
If your application already exposes its functionality through APIs and structured state, you may already possess most of what is required to add a conversational layer on top of it.