The Growth Brief

iterable

How Nova Send Time Optimization picks a per-person send time, and when to turn it off

How Iterable Nova Send Time Optimization picks a per-person send time, and the Quiet Hours and sendAt guardrails that override it.

June 8, 20266 min readKodie Critzer
BRCG

ITERABLE

ITERABLE

The Growth Brief

Send Time Optimization sounds like a switch you flip and forget. In practice, the trouble is never the slot it picks for a well-engaged subscriber. It is the three guardrails sitting underneath it, because Quiet Hours, an explicit `sendAt`, and a thin-history fallback can all quietly override the time you think STO chose. Lifecycle teams running Iterable get surprised by this when a send that "had STO on" goes out at a flat time anyway, and nobody can explain why.

This post walks through how Nova STO actually decides a per-person time, then the three interactions that change that decision. For Iterable specifics, I'm leaning on Iterable's own documentation; the opinions and the setup order are ours from running this in production.

Key takeaways

  • Nova STO reads each recipient's open and click history and picks a top-of-the-hour slot for email and push, retraining weekly.
  • It discounts opens that look machine-generated, including Apple Mail Privacy Protection inflated opens, so the model doesn't learn a fake 6am peak.
  • An explicit `sendAt` on a send disables STO for that send. They don't stack.
  • Quiet Hours sits on top of STO and defers any optimized slot that lands inside the window, usually to the window's end.
  • Recipients with roughly under three months of usable history fall back to your configured default time, not a personalized one.

What STO is actually doing

Nova Send Time Optimization is the part of Iterable's Nova AI suite that chooses *when* to send, per recipient, for email and push. The model looks at an individual's historical engagement, mainly opens and clicks, finds the hour they're most likely to engage, and schedules the send for the top of that hour. So instead of one blast at 10:00am for everyone, you get a spread: 7:00am for the early commuter, 9:00pm for the night-shift reader.

Two details matter more than the headline.

First, it retrains weekly. STO is not a one-time computed trait baked in at signup. Engagement patterns drift, people change jobs, and the model re-fits on a rolling cadence. A subscriber who shifted from morning to evening reading will migrate to a later slot over a few weeks without you touching anything.

Second, it tries not to be fooled by fake opens. Apple Mail Privacy Protection pre-fetches images, which fires an open event whether or not a human looked at the message. If you trained naively on raw opens, MPP users would all cluster at whatever hour Apple's proxy happens to fetch, and you'd "optimize" toward a phantom. Iterable's docs note that STO discounts these inflated opens. This is also why STO leans on click signal where it can, since a click is far harder to fake than an open. Practical consequence: for a heavily iOS audience, your STO slots are being driven more by clicks than the open rate dashboard would suggest, and that's a good thing.

The decision flow, including the guardrails

Here's the part teams miss. STO doesn't run in a vacuum. Three things sit in front of or on top of it.

The order of checks that determines a recipient's final send time when STO is enabled.
The order of checks that determines a recipient's final send time when STO is enabled.

1. An explicit sendAt disables STO

This is the one that burns people. If your send specifies a hard `sendAt` timestamp, that wins. STO does not get to pick a slot, because you already picked one. They are mutually exclusive on the same send. The mistake we see is a team enabling STO at the campaign level while a triggered journey node or an API call still passes a `sendAt`, then wondering why everyone got the message at the same minute. If you want STO, do not also hand Iterable a fixed time.

2. Quiet Hours clamps the slot

Quiet Hours is a separate guardrail you configure to keep messages out of, say, 10pm to 7am local. STO runs first and picks an hour. If that hour lands inside the Quiet Hours window, the send doesn't fire at the optimized time. It gets deferred, typically to the end of the window. So a recipient whose true peak is 11pm won't get an 11pm push if Quiet Hours starts at 10pm. They get bumped to 7am.

This is correct behavior, but it has a side effect worth naming: if a meaningful chunk of your audience has true peaks inside Quiet Hours, you'll see a pile-up at the window's end. That 7:00am spike isn't STO failing. It's Quiet Hours doing exactly its job, and the two interacting. Check your post-send timing histogram before you blame the model.

3. Thin history falls back to the default time

STO needs enough signal to find a peak. Iterable's docs describe a fallback for recipients without sufficient history, roughly under three months of usable engagement. Those users don't get a personalized slot; they receive the campaign at the configured default send time. So a brand-new list, or a welcome program firing on day one, is largely sending at your default time regardless of the STO toggle. STO earns its keep on your established base, not your fresh signups.

A concrete walkthrough: two API sends

Here's the difference between letting STO decide and overriding it, using Iterable's send API shape. Same campaign, same recipient, opposite outcomes.

json
// Send A — STO decides the time. // No sendAt. Campaign has Send Time Optimization enabled. POST /api/email/target { "campaignId": 4451029, "recipientEmail": "reader@example.com", "dataFields": { "firstName": "Jordan" } } // Send B — explicit sendAt. STO is bypassed for this send. POST /api/email/target { "campaignId": 4451029, "recipientEmail": "reader@example.com", "sendAt": "2026-06-09 14:00:00", "dataFields": { "firstName": "Jordan" } }

In Send A, Iterable resolves the per-person slot (or the fallback default if history is thin), then clamps against Quiet Hours. In Send B, the message fires at 2:00pm in your project's send-time interpretation, and STO never gets consulted. If you genuinely need both a fixed business moment (a flash sale ending) and personalized timing on other sends, split them into separate campaigns rather than fighting the override on one.

When we turn STO off

STO is not a default-on for everything. We leave it off, or override it, in a few cases:

  • Time-bound sends. A sale that ends at midnight, an event reminder two hours before doors. The business moment beats the engagement peak. Use `sendAt`.
  • Transactional and triggered-now messages. Password resets, receipts, shipping updates. These go immediately; there's no slot to optimize.
  • Programs dominated by new users. If most of the audience is under the history threshold, STO is mostly handing back your default time anyway. Set a smart default and revisit once the base matures.
  • Tight operational coordination. When email, SMS, and a paid push need to land in a coordinated sequence, spreading sends across everyone's personal peaks breaks the choreography.

Everywhere else, on your engaged, established base, with Quiet Hours set sensibly and no stray `sendAt` in the journey, STO is worth running. We measure it the same way we measure any Nova feature: against a held-out control. It earns its place where it moves engagement, and we turn it off where it doesn't.

FAQ

Does an explicit sendAt disable Iterable Send Time Optimization? Yes. If a campaign or API send specifies an explicit `sendAt` timestamp, that send fires at the time you set and STO does not pick a per-person slot. They are mutually exclusive on the same send.

What happens to STO for a recipient with little engagement history? Iterable's docs describe a fallback: recipients without enough open and click history, roughly under three months, don't get a personalized slot. They receive the campaign at the configured default send time instead.

How do Quiet Hours interact with Send Time Optimization? Quiet Hours acts as a guardrail on top of STO. If STO selects a slot inside a recipient's Quiet Hours window, the send is deferred, typically to the end of that window, so the optimized time gets clamped rather than honored exactly.

We build, tune, and measure Nova features like STO in production as part of our Iterable partner work. If you want a second set of eyes on your STO and Quiet Hours setup before you trust the timing histogram, book a call.

Want this in your inbox?

No newsletters that apologize for being newsletters. Just sharp takes on what's working in growth.

Book a growth call