Building an internal agent: Triggers
An internal agent only provides value when its workflows are initiated. Building out a library of workflow initializations, which we call triggers, is a core part of building an internal agent.
This is part of the Building an internal agent series.
Why triggers matter
While there’s a lot of chatter about AI empowering employees to trivially automate their day-to-day workflows, building these workflows requires an intuition about how agents work that requires iterative learning to develop, and few workplaces are providing the tools to facilitate that iterative learning. Easy triggers, combined with easy prompt iteration, are a foundational part of iterative learning.
More practically, triggers are also the mechanisms that initiate workflows, so nothing else matters if they aren’t effective and usable.
Why not Zapier or n8n?
The obvious question here is “why not Zapier?” and “why not n8n?”, both of which would have solved this triggering problem in its entirety. For what it’s worth, you still could use Zapier or n8n to trigger agent workflows using a custom webhook trigger, so these approaches aren’t incompatible. That said, for me there are only a small number of workflows I thought would matter, and I wanted to have full control of the nuances as we worked on the problem.
The “full control” piece ties back to one of my underlying theses of this work: the quality details facilitate adoption in a way that Zapier integration’s constraints simply do not. This is why I think internal agents need to spend so much time managing things like Slack-entity resolution to become a seamless experience.
How we implemented triggers
We’ve implemented these triggers, and in this order:
Notion webhooks can be configured to fire on any page or database modifications, including things going from “draft” status to “ready for review” status or other more nuanced changes. We’ve updated all our Request for Comment and other structured document databases to hook into these.
The triggers drive a variety of workflows. On the simpler end, they can be used to assign additional reviewers to a document based on topic, and on the more complex end they can use a prompt to provide in-line comments with suggestions.
Slack messages can trigger responses or other workflows. This is dependent on which channels the bot has been invited into, and I subsequently made a trigger to capture channel creation to support auto-joining new channels to increase availability. (Channels that are auto-joined have a simple, default prompt that only responds when the bot is mentioned directly, to avoid wearing out its welcome.)
We also got this working in private channels (not the auto-join component, just responding to messages). The hard part was ensuring that logging didn’t capture any evidence of joining or responding in those private channels. Exfiltrating private messages would have been a very easy way to quickly lose trust.
Jira webhooks can be configured to trigger notifications on issue creation, updates, comments and so on.
Slack reacji were a later addition, where we added support for listening to Slack reacji in either a given channel or in any channel where the bot is present. This has made it possible to quickly implement a trigger where the
:jira:reacji in any channel turns a thread into a ticket, routing it using centralized routing instructions (which are imperfect, but much better than the average individual’s ability to route across many different teams).Scheduled events are our most recent addition, allowing periodic triggers. I’ve done two distinct v0 implementations, with the first relying on Slack workflows publishing into new channels as triggers, and the other relying on Github actions. Both work well enough, despite being a bit messy.
Now that we have a fairly large category, and have updated our AGENTS.md to reflect our existing integrations,
adding new sorts of triggers is generally pasting documentation into Claude and waiting a few minutes.
Trigger authn and authz
When it comes to authentication and authorization, where possible, we’ve adopted the OAuth2 approach of authorization tokens and SSL. However, that isn’t possible for scenarios like Slack where we can’t inject an authorization token into the requests. In those situations we rely on the application-specific security mechanisms provided by the underlying platform, such as Slack’s mechanisms for verifying requests.
How is it working? What’s next?
Our current set of triggers are working well. The next task here–probably a two sentence prompt and ten minutes of Claude code away–is adding a trigger on more generic webhooks that includes the whole incoming message into the context window. This hasn’t been necessary yet, but is a useful catch-all to support future workflows.
In particular, it would be straightforward to pair with an existing Zapier subscription, which would offload supporting certain esoteric triggers to their large catalog of integrations, while still getting the control and nuance of the internal agents.