Serverless

Send your events later

Send your events later

I build a lot of automated workflows. Across the suite of apps that powers Ready, Set, Cloud, I have a dozen or so mature, long-running automations that do some work, wait for something, then do more work. A great example is my newsletter. I often write it on Fridays and publish it on Mondays. After I open a PR for an issue, my backend service does some initial work, but it waits until Monday to send the emails and start analytics tracking.

In the past, I’ve used the Step Functions Wait state for this because it’s the exact use case it was designed for. But as part of a big refactor I did to separate Ready, Set, Cloud logic from the generic newsletter logic, I needed to find a simpler way to schedule work - ideally as part of my CI/CD pipeline.

I thought using the AWS SDK in a script to create a one-time EventBridge schedule that published an event at a designated time was my best bet. But that brings in some implications with IAM and passing roles to the scheduler in order to invoke a PutEvents call. It ended up being way too messy for a simple CI script.

But I had an epiphany that immediately made me think, “I should write about this” because it’s very simple but not always obvious.

The surprisingly-simple-yet-not-obvious solution

I moved the EventBridge Scheduler event publish behind an EventBridge rule. Anything that can call PutEvents can now ask for a deferred event.

flowchart LR
  A[Any app in the account] -->|Schedule Event| B[(Event bus)]
  B --> C{detail-type is<br/>'Schedule Event'}
  C --> D[ScheduleEventFunction]
  D -->|one-time, self-deleting| E[EventBridge Scheduler]
  E -.->|Scheduled time, e.g.<br/>at 9:00 AM Central| B
  B --> F{detail-type is<br/>whatever was<br/>scheduled}
  F --> G[Existing consumer]
    

It’s essentially a one-time loop with a customizable wait step in it. Here’s an example payload of an event to get my static site to rebuild because future-dated content doesn’t render automatically.

{
  "Source": "readysetcloud.app",
  "DetailType": "Schedule Event",
  "Detail": {
    "at": "2026-08-03T09:00:00",
    "timezone": "America/Chicago",
    "name": "newsletter-rebuild-227",
    "event": {
      "detailType": "Trigger Site Rebuild",
      "detail": { "issueNumber": 227 }
    }
  }
}

Publishing the above event will create a schedule that triggers at 9 a.m. CDT on August 3. The schedule will publish a Trigger Site Rebuild event back onto the bus, which is wired up to a rule that kicks off a build in Amplify. Nifty, right?

Just a little bit of logic

While I normally try to skip Lambda in this type of integration, I needed it for a couple of reasons. One, you can’t set the EventBridge Scheduler as a target of a rule 🙃. And even if you could, I would have lost my ability to maintain schedules. If I needed to reschedule an event for a different time, I would have been out of luck.

So I wrote a Lambda function as the target that will upsert the schedule, validate the event (WHICH YOU SHOULD ALWAYS DO), and transform the provided time to the format the scheduler expects.

As a bonus, the Lambda function also recognizes when the scheduled date is in the past, and will publish the event immediately or throw it away, depending on a parameter optionally provided in the event payload.

So in this case, it made a lot of sense to add the function in the middle, which increases the complexity of the build, but the tradeoff is a richer experience.

Build your own platform

A few years ago, I made what I called my serverless toolbox, with some tools I could use across all of my apps that ran in the same AWS account. What I didn’t know then was that I was essentially building a platform for all of my applications to run.

I’ve since abandoned the toolbox and have turned my attention fully toward building a platform my apps run on. It’s the epitome of DRY (Don’t Repeat Yourself) and incredibly useful in this case. I’ve added this scheduling primitive to my platform repo so it can be used everywhere - my newsletter service, blog service, and all the other services I’ve built that power my website.

I only need to solve this problem once. Time and time again I’ve reimplemented things that worked in the past only to find myself with 4 versions of the same thing and at least one of them stale. So I make an intentional effort to consolidate the shared work and make the entry point to it as simple as possible (what’s simpler than calling PutEvents?!).

Anyway, this is intentionally short and sweet. I was excited by the idea of delaying an event with nothing more than PutEvents, and wanted to share. For the full source code, you can check out the function here or view the entire PR to my platform app.

Happy coding!

Allen Helton

About Allen

Allen is an AWS Serverless Hero passionate about educating others about the cloud, serverless, and APIs. He is the host of the Ready, Set, Cloud podcast and creator of this website. More about Allen.

Share on:

Join the Ready, Set, Cloud Picks of the Week

Weekly writing and curated picks on cloud-native systems and practical AI. Browse past issues to see if it’s for you.
Browse past issues.

Join the Ready, Set, Cloud Picks of the Week

Thank you for subscribing! Check your inbox to confirm.
View past issues.  |  Read the latest posts.