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!