Configuring Backup Notifications

Sym gives you the ability to define an arbitrary number of backup destinations for a request, and configure timeouts between each.

Overview

When making access requests, it is possible to specify that you would like the initial request destination to have one or more backups. This is useful in the case of:

  • Message delivery failure (e.g. a destination channel has been deleted)
  • Failure to respond to a request (e.g. your first intended responder is on PTO)

Terminology

TermDescription
message delivery failureA message cannot be sent to a request destination for whatever reason (e.g a Slack channel does not exist).
timeoutHow long (in seconds) a request notification will stay open until it either gets forwarded or expires.
request forwardA new request notification gets sent due to a timeout or message delivery failure.
request expireWe have exhausted all request forward destinations and the last one has timed out; the request terminates without a response and is no longer actionable.

Implementation

In order to use the backup notifications feature, your get_request_notifications Reducer must return a list of at least 2 Notification objects. The Notification object has an optional timeout flag that takes an integer representing seconds until the request will time out and fall back to the next Notification object in the list. If a message fails to send, the next Notification in the list will be called.

Example SituationsIs a Failure?
All destinations in a Notification fail to sendTrue
Some destinations in a Notification fail to send, but at least one destination succeedsFalse
User notifications cannot be delivered because the user has opted out of all contact methods (e.g. email).False

Example

from sym.sdk.annotations import reducer
from sym.sdk.notifications import Notification
from sym.sdk.integrations import slack, sym

@reducer
def get_request_notifications(event):
    return [
        Notification(destinations=[slack.channel("#sym-requests")], timeout=1800),
        Notification(destinations=[slack.channel("#sym-backup"), slack.user("@cassie")], timeout=900),
        Notification(destinations=sym.get_or_create_users_by_emails(["[email protected]", "[email protected]"]))
    ]

In this example, assuming no delivery failures:

  • The request will be sent to the #sym-requestschannel.
  • If there is no response within 1800 seconds (30 min), the request will be sent to the #sym-backup Slack channel and as a Slack DM to the user @cassie.
  • If there is no response within 900 seconds (15 mins), the request will be sent to [email protected] and [email protected] via their preferred channels (e.g. email).

Advanced: Events

As with all transitions in the Sym SDK, request notifications expose hooks to the SDK.

It is important to note that the Events that occur in the SDK will be slightly different depending on the behavior specified by the Notification objects returned by get_request_notifications.

In general:

  • When a timeout occurs and there is another Notification in the list, Sym will fire a request_forward Event.
  • When delivery failure occurs, no new Event will be fired. Sym will simply try the next notification without firing any Events.
  • When we have reached the end of our list of Notification objects and the last one times out, Sym will fire a request_expire Event.

Just like other Events, you can write a hook in your impl.py with custom Python logic that will run on_ or after_ the request_forward and request_expire Events.

πŸ“˜

Note: request_forward hooks accept an additional argument request_forward_context (RequestForwardContext) which contains information about the full forwarding context of a request's notifications. This includes both the full list of potential Notification objects as well as its current index in moving through that list.

For example, you might want to DM a requester each time their request is forwarded:

from sym.sdk.annotations import hook
from sym.sdk.integrations import slack


@hook
def on_request_forward(event, request_forward_context):
    index = request_forward_context.current_destination_index
    current_notification = request_forward_context.all_notifications[index]

    if current_notification:
        slack.send_message(
            event.get_actor("request"),
            "No one responded to your request yet, so we forwarded it!"
        )