EmailTimer.App

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 grey "image unavailable" card.

Where each merge tag goes, and what comes back
Where each merge tag goes, and what comes back In the email platform, the coupon code merge tag is in the link and the first name merge tag is in the alt text. The image source is a contact field holding an address signed for that subscriber. The render engine reads that subscriber's token and draws their name, their code, a countdown, a QR code and a VIP badge shown by a rule into one image. 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 loops or it holds, whichever you set in the editor. 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
How one image request is answered Four stages. The recipient's mail client requests the image address, the image service checks the signature and loads the published version, the render engine draws the frames for the moment of the request, and the image goes back with HTTP 200 whatever happened on the way. 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 or holds 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 grey "image unavailable" card, 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