Workivo Docs: Agents
Agents
Build automated agents using the tools already in Workivo. No new code needed for most use cases — configure tables, jobs, and rules through the UI.
Anatomy of an agent
Every agent is just three things wired together:
- Config table — a custom table to store the agent's settings, state, and results
- Job — a recurring Ruby script that does the work (checks something, sends something, processes data)
- Logs — the agent writes to Log records so you can see what happened
Step by step
1. Create the config table
Go to Tables → Create table (e.g. weather_checks). Add whatever columns you need:
name(string) — label for this agent instancecity(string) — what to checkthreshold(float) — alert thresholdlast_result(text) — latest outputlast_checked_at(datetime) — when it last ran
2. Define the form and list
After creating columns, go to the table's list page and click New Record. The system auto-generates a form from your columns. Tweak it by editing the form definition to hide internal fields, add help text, or change types.
3. Create the job
Go to Jobs → New Record. Set:
- Name — describes what it does
- Run type — Recurring (every N minutes)
- Every (minutes) — how often it checks
- Code — the Ruby script (see examples below)
4. Write the agent code
The job's code runs via bin/rails runner. It has full access to models, the database, and external APIs. Common patterns:
Check something and log it
WeatherCheck.find_each do |record|
temp = fetch_temperature(record.city)
Log.create!(
message: "#{record.city}: #{temp} F",
source: "agent.weather",
context: { city: record.city, temp: temp }.to_json
)
record.update!(last_result: temp.to_s, last_checked_at: Time.current)
end
Send an alert when threshold is met
WeatherCheck.find_each do |record|
temp = fetch_temperature(record.city)
if temp > record.threshold
Log.create!(
message: "ALERT: #{record.city} at #{temp}F (threshold: #{record.threshold})",
source: "agent.weather.alert"
)
end
record.update!(last_result: temp.to_s, last_checked_at: Time.current)
end
Process data from another table
Log.where("created_at < ?", 7.days.ago).delete_all
Log.create!(message: "Cleaned logs older than 7 days", source: "agent.cleanup")
5. Monitor via logs
All agent output goes to Logs. Use the source field to filter — e.g. agent.weather shows only weather agent activity. Create a custom list definition for Logs filtered to a specific source if you want a dedicated view.
6. Wire up the sidebar
Go to Sidebar Items → New Record. Add a link to your agent's list page so it's one click away from the nav.
Going further
Rules for event-driven agents
If your agent needs to react to record changes (not just run on a schedule), use Rules. Create a rule file in app/services/rules/your_table/your_rule.rb. The rule fires on create/update/destroy of records. See Rules docs for the full pattern.
External APIs
Job code runs in a full Rails context. You can use Net::HTTP, Faraday, or any gem. Add it to the Gemfile, bundle install, restart, and use it in your job code.
Testing in the console
Use the Console to test agent code interactively before saving it as a job. Run your script line by line, inspect variables, and verify it works.
Forms with HTML widgets
For agent config tables, use the html field type in the form definition to show computed previews, status badges, or links. Template variables like {{record.last_result}} render live values on the record view.
Example: a weather monitor agent
Here's what a complete agent looks like using only the UI:
- Create table weather_checks with columns: name, city, threshold, last_result, last_checked_at
- Add a record: city=Chicago, threshold=90
- Create a recurring job (every 10 min) with code that fetches weather and logs results
- Check Logs to see it running
The entire agent is configurable from the UI — no deployment, no migrations beyond the initial table creation.
Back: