Optimizely Opal: How to Build Effective Workflow Agents
If you're building workflow agents in Optimizely Opal, this post covers how specialized agents pass context to each other, why keeping agents small and focused matters, and a useful trick for handling situations where you're not sure how to proceed.
I recently decided to put Optimizely Opal through its paces. The experiment: build a simple mock API, feed external data into Opal, and see if a workflow agent could take it from there and autonomously publish content to the CMS, with no human intervention required.
The workflow agent I built pulls data from an external mock API on a schedule, analyzes it, checks it against existing content in the CMS, makes a judgement call on whether a new article is warranted, and if it is, writes and publishes it to the CMS, all without any human intervention.
It is made up of 5 specialized agents, each with a single responsibility, and a condition that branches the workflow based on whether a new article is warranted.
My example consists of the following:
- An agent that retrieves route information from the mock AviationStack API
- An agent that compares the route info with articles published in the CMS, and makes a judgement call on whether the info from the API call warrants a new article in the CMS
- An agent that writes an article based on the info received from the external mock AviationStack API
- An agent that publishes the new article to the CMS
- An agent that gracefully ends the workflow if no new article is needed

The full workflow agent. A scheduler triggers the process, data flows through each specialized agent in sequence, and a condition branches the workflow: TRUE continues to writing and publishing, FALSE ends the workflow cleanly
If you want to build a similar workflow agent yourself and apply some of the tips here in practice, here’s what you need:
- Optimizely Opal
- Access to Optimizely CMS
- A mock API with a Custom Tool configured in Opal
- Basic familiarity with creating specialized agents in Opal
Here's what I learned
There’s no prompting between specialized agents
Each agent passes its output directly to the next and runs completely on its own, with no human in the loop. This keeps the workflow consistent and predictable, but it also means you need to be deliberate about what each agent outputs, because the next agent depends on it entirely.
Agent 1 output:

The first specialized agent outputs a simple JSON object containing the route information retrieved from the mock AviationStack API
Agent 2 Input:


That same "routes" object becomes the input for the next agent, defined as a required variable and referenced directly in the prompt template. This is what "output becomes input" looks like in practice.
Create as many specialized agents as you need
Don't hesitate to create as many agents as your workflow needs, and be as granular as necessary. Each agent should do one thing well, not many things adequately.
Developers will recognize this as the Single Responsibility Principle (SRP), and it applies just as well here. For those less familiar with the term, the idea is simple: don't create one agent that retrieves external data, analyzes it, writes an article, and publishes it. That's too much for one agent to handle reliably, and you'll end up with bloated instructions. Split those responsibilities across separate agents instead, and let them pass context to each other.

A single agent handling too many responsibilities. This is harder to debug, harder to maintain, and more likely to produce inconsistent results.

The same workflow split across three focused agents, each with a single responsibility, and each passing its output to the next.
If you run into a gray area and you're not sure how to handle it, create a specialized agent
This is simpler than it sounds. When you hit a question like "How do I get it to write an article?", you create a specialized agent for it. "How do I get it to publish to the CMS?", you also create a specialized agent for it. Most problems in a workflow agent have the same answer: a focused, purpose-built specialized agent.
A good example from my own workflow: when the condition evaluates to false, meaning the external data doesn't warrant a new article, I needed the workflow to simply stop cleanly. There's no built-in "do nothing" option, but the answer was straightforward: create an "End Workflow" agent whose only job is to acknowledge the decision and exit gracefully.
The "End Workflow" agent keeps it simple by design. No tools, no content creation, no external calls. Its only job is to end the workflow cleanly when no action is needed, which is exactly the kind of focused single-responsibility agent your workflow will thank you for later.
Don't assume an agent has context from earlier in the workflow unless you explicitly pass it
This is an important one. Just because multiple specialized agents live inside the same workflow agent doesn't mean they automatically share context. If Agent 4 needs data that Agent 1 retrieved, you need to explicitly pass it through each agent's inputs and outputs along the way, it won't be there by default.
In my workflow, by the time the article-writing agent needs to do its job, it has no automatic awareness of the route data retrieved at the start. If I want it to have that context, I need to explicitly include it in the outputs of the agents before it, and specify it as an input to the agents that follow.

The output schema of my "Gap Analyzer" agent explicitly carries the "routes" object forward, alongside its own "reason" and "publishDecision" fields. Without "routes" being defined here as an output, the article-writing agent downstream would have no access to the original API data, even though it was retrieved earlier in the same workflow.
That's about it for now! Hope this helps someone else out. If you've built something similar or have any questions or suggestions, I'd love to hear about it in the comments. Thanks for reading!
May 20, 2026
Comments