EmailTimer.App

Menu

How email countdown timers and dynamic images work

An email cannot run JavaScript, so a countdown has to arrive as an image that is drawn at the moment the mail app asks for it. This is how that works.

Step 1 / the snippet

What you paste into your campaign

A timer reaches your subscribers as one image inside a link, and the few lines of HTML that carry it are the snippet. You copy it from the Snippet tab when you publish, after picking your email platform, so the link and the alt text already carry that platform's merge tags.

The image address in the snippet is signed, so nothing in it can change after you copy it. That is why a subscriber's first name goes in the alt text and not in the address. If your platform filled a merge tag into a signed address, the signature would stop matching and every subscriber in the send would get a blank, transparent image from the Worker in front of the image service.

Where each merge tag goes, and what comes back

Your email platform

Klaviyo syntax, for example:

<a href="…?c={{ coupon }}"><img src="{{ person.et_url }}"alt="{{ first_name }}, 20% off">

Filled in at send:

Sarah's code, name, signed address

Render engine

Reads Sarah's recipient token

Layer 1: Text, "Sarah, 20% off"

Layer 2: Her code, "SARAH20"

Layer 3: Countdown, 30 frames

Layer 4: QR code, her claim link

Rule: show the VIP badge by tier

The image she sees

Sarah, your exclusive pass

SARAH20, 20% off

03 : 42 : 19

One file, drawn when she opens

Drawn for one image per subscriber, on Growth and Agency: the image source is a contact field holding an address signed for that subscriber. With one image for the whole send, the merge tags go in the link and the alt text, and the image carries what is the same for everybody.

Step 2 / the open

What happens when a subscriber opens the email

Their mail app asks for the image, and that request is one open, which is what your plan counts. The time left is worked out at that moment, so two subscribers who open an hour apart see two different numbers from the same snippet.

The image is an animated GIF of 30 frames by default, one a second, so the seconds move for half a minute. After the last frame it starts again from the first. The first frame is a complete image with the right time on it, because Outlook on Windows shows that frame and nothing after it.

How one image request is answered

1. The open

Email client requests <img src="...">

Signed query: w, v, r, wm, sig

One HTTP request

HTTP GET

2. Image service

Checks the address

Verifies the signature

Loads the sent version

Reads the token, if any

Bad signature: fallback

Verified

3. Render engine

Works out the time left

30 frames, one a second

Frame 1 is a whole image

Draws your text and font

Past the deadline: expired

200 OK

4. The response

A GIF or a PNG

Cache-Control header

Plays in the inbox

Then loops

Never an error code

Step 3 / the first open, and after it

How Apple Mail and Gmail handle image requests

Some mail apps do not ask for the image when the subscriber opens the email. Apple Mail with Mail Privacy Protection collects it when the message is delivered, so those subscribers see the time that was left at delivery. Gmail asks through its proxy at the first open, and a re-open can come back from the copy the proxy kept. Knowing which client does what tells you which timing model to pick.

How email clients fetch and cache a timer

Apple Mail (MPP)

Pre-fetches at delivery

Fetches image immediately when message hits iCloud servers.

Cache: 2 to 3 days (reported)

Timer reflects remaining time at delivery, not open moment.

What EmailTimer.App does:

Fixed deadlines, and a preview per client before you publish

Gmail (Google proxy)

Fetches on first open

Requests image through Google image proxy at the first open.

Cache: lifetime not published

First open plays live 30s GIF. A re-open can read the cache.

What EmailTimer.App does:

Draws the image when the proxy asks, so a first open is current

Outlook on Windows

First frame only

Uses Microsoft Word engine. Does not animate GIF frames.

Cache: not documented

Renders frame 1 as static graphic in reading pane.

What EmailTimer.App does:

Frame 1 is a whole image, with the time left at the moment of the fetch

Apple documents the prefetch and Google documents the proxy. Neither publishes how long its cache holds a copy. The two to three days is from FreshInbox's 2021 testing of Mail Privacy Protection, not from Apple.

What each client is documented to do

Email client Fetch moment Cache lifetime GIF animation What EmailTimer.App does Basis
Apple Mail (iOS 15+, macOS Monterey+) Apple Mail Privacy Protection (MPP) Delivery time (pre-fetched by Apple server) Testing reports 2 to 3 days Full 30-second live animation The timer counts down from the time remaining at delivery. A fixed deadline, such as 23:59 UTC, is still the right deadline. Documented by Apple
Gmail (web, iOS, Android) Google image proxy First open by recipient Not published by Google Full 30-second live animation First open displays a real-time countdown. A re-open can be served from the copy the proxy already holds. Documented by Google
Outlook on Windows (2016 to 2024) Word rendering engine Open time Not documented First frame only (static image) Frame 1 is a complete image with the time left at the fetch, so a reader who sees only that frame sees a correct still countdown. Expected, not yet documented or tested
Outlook on the web and mobile (iOS, Android) Native browser or app engine Open time Not documented Full 30-second live animation Expected to play the animation, 30 frames at one frame per second by default. Expected, not yet documented or tested
Yahoo Mail and AOL Yahoo image proxy Open time Not documented Full 30-second live animation Expected to fetch the image when the message is opened and play the animation. Expected, not yet documented or tested

The Apple and Gmail rows rest on Apple's and Google's published documentation and on third-party testing. Neither company publishes a cache lifetime: Apple documents the prefetch, Google documents the proxy, and the two to three days is FreshInbox's 2021 testing. The Outlook and Yahoo rows are what we expect those clients to do, with no vendor document behind them. No row comes from a test by us yet, and we will publish dated screenshots when we have them. Sources: Apple on Mail Privacy Protection, Google on the Gmail image proxy, FreshInbox on Mail Privacy Protection image caching.

The publish panel in EmailTimer.App showing Gmail on first open, Gmail opened again, Apple Mail with privacy and Outlook on the desktop, each with the clock it would show
The same four cases inside the product, before a template is published. The badge on each card says how far that client's copy is from the real deadline.

Step 4 / after the deadline

What happens when a timer reaches zero

Once the deadline has passed, the image stops counting and becomes the expired frame you chose in the editor, under When it expires: the timer at all zeros, or an image of your own, such as a note that the offer has ended. Subscribers who open late read that instead of a clock stuck at zero.

For your developer

The protocol, for whoever asks how it is built

Mail clients block JavaScript, iframes and WebSockets. The one dynamic thing an email can carry is an <img> whose src is fetched when the message is displayed, so that fetch is where all of this happens.

The address is /i/{template}.gif or .png on img.emailtimer.app. Its query carries w for the workspace, v for the published version the email was sent with, r for a recipient token when there is one, wm for the over-quota watermark, and sig, an HMAC-SHA256 over the path and the canonical query. A value that genuinely differs per subscriber is looked up from their recipient token, which travels in a signed address made for that subscriber through the API or a recipient list and carried in a contact field.

The digits and the static artwork of a timer are drawn apart from the clock and reused. An open works out the time left and copies the pre-drawn digits into the frames, each shown for 100 centiseconds, with frame 1 carrying the whole design. Every request is answered with HTTP 200 and a valid image: a bad signature gets a blank, transparent image from the Worker in front of the image service, a passed deadline gets the expired frame, and the Cache-Control header differs for a live timer, an expired one and a static image.

Where this goes into more detail

The complete guide to countdown timers in email

Every email client, all six timing models, and the setup for each platform, in one page.

Read the guide