Title: Send your events later Publication: Ready, Set, Cloud! Author: Allen Helton Published: July 29, 2026 URL: https://www.readysetcloud.io/blog/allen.helton/send-your-events-later/ Turn deferred event delivery into an EventBridge primitive so any app can schedule work without managing EventBridge Scheduler itself. 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](/newsletter/sign-up). 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](https://docs.aws.amazon.com/step-functions/latest/dg/state-wait.html) 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. ```mermaid flowchart LR A[Any app in the account] -->|Schedule Event| B[(Event bus)] B --> C{detail-type is
'Schedule Event'} C --> D[ScheduleEventFunction] D -->|one-time, self-deleting| E[EventBridge Scheduler] E -.->|Scheduled time, e.g.
at 9:00 AM Central| B B --> F{detail-type is
whatever was
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](https://www.readysetcloud.io/blog/allen.helton/serverless-post-scheduler-for-static-sites/). ```json { "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](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-targets.html) 🙃. 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*](/blog/allen.helton/automate-your-life-and-save-time-with-serverless-technology), 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](https://github.com/readysetcloud/rsc-core) 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](https://github.com/readysetcloud/rsc-core/blob/main/functions/schedule-event.mjs) or view the [entire PR](https://github.com/readysetcloud/rsc-core/pull/223) to my platform app. Happy coding!