Workflow Handlers
Use Sym's workflow handlers to route, streamline, and automate you Flows with custom logic.
Overview
Under the hood, each Sym Flow
consists of a series of discrete steps that can be interrupted or redirected with custom logic wrapped in Handler functions. The two types of Handlers are reducers and hooks.
Reducers are prefixed with get_
, and take in an Event
and return a single value.
Hooks are prefixed with on_
or after_
, and allow you to alter control flow inserting custom logic before or after an Event
is processed.
Handlers in context
Handlers fire at specific moments in a Sym Flow, giving you the power to define things like:
- Where requests are routed
- Scenarios in which requests are automatically approved or rejected
- How to handle third-party identity lookups
- What to do after a request cycle is completed

Handlers are defined in the
sym.sdk.annotations
module.
Reducers
A Reducer injects key logic into Flows
by taking an Event
as input, and returning a single value.
For example, when using the sym:approval
Template
, you need a way to let Sym know who to route a specific request to. This is exactly what the get_approvers
reducer does! To implement it, you write a Python function which takes in an Event
(containing the requesting user and requested Target, and return a SlackChannel indicating where to route the request.
from sym.sdk.annotations import reducer
from sym.sdk.integrations import pagerduty, okta, slack
@reducer
def get_approvers(event):
"""Returns a set of approvers, given a user and a target."""
if pagerduty.is_on_call(event.user, schedule_name="prod_on_call_schedule"):
# This is a self-approval in a DM
return slack.user(event.user)
if event.payload.fields["urgency"] == "High":
# This is a self-approval in a channel
return slack.channel("#break-glass", allow_self=True)
on_call_mgrs = okta.users_in_group(group_id="00g12345689")
# This would create a group DM for on-call managers
return slack.group(on_call_mgrs)
On-Hooks
Hooks are executed before the default implementation of an Event
handler in a Template
. They offer an opportunity to bypass, short-circuit, or alter control flow, by emitting Events
.
For example, when using the sym:approval
Template
, you may want to auto-approve any requests that come from an on-call engineer.
from sym.sdk.annotations import hook
from sym.sdk.integrations import pagerduty
from sym.sdk.templates import ApprovalTemplate
# Hooks are optional, and can change control flow by returning Events
@hook
def on_request(event):
# Auto-approve urgent requests for access by the person on call
if event.payload.fields["urgency"] == "Urgent" and pagerduty.is_on_call(event.user):
return ApprovalTemplate.approve()
After-Hooks
After Hooks are executed after the default implementation of an Event
handler in a Template
. They offer an opportunity to execute additional side-effects, such as logging or notifications.
For example, when using the sym:approval
Template
, you may want to log every approval to a private Slack channel.
from sym.sdk.annotations import hook
from sym.sdk.integrations import slack
# After-hooks are optional, and let you execute side-effects after an Event
@hook
def after_approve(event):
"""Executed after an approved event has been fired."""
message = f"{event.user.username} has been approved for {event.payload.fields["target"].name}!"
slack.send_message(slack.channel("#private-audit-log"), message)
Updated 6 months ago