EmailTimer.App

Countdown timers and personalized images for Braze email

Your subscribers see how long is left on the offer, counted from the moment they open the message, and it goes into the Braze campaign you have already built. One snippet, in the merge syntax Braze already uses.

Animated countdown in the digital style, reading 02 days 03 hours 34 minutes 12 seconds and counting down
A render from the engine at 600 by 200, the size that goes into a Braze image block.

Braze merge syntax

The tags you already use, around the image

The same merge fields that put a subscriber's name in your subject line go in the link and the alt text around the image, where Braze fills them in as it sends. They never go inside the image address, for the reason in the warning further down. A name or a deadline drawn inside the image itself comes from an address signed for each subscriber, held in a contact field.

First name
{{${first_name}}}
Coupon code
{{custom_attribute.${promo_code}}}
Deadline
{{custom_attribute.${deadline_iso}}}
Email address
{{${email_address}}}

Checked against Braze's own documentation: Supported Liquid personalization tags (Braze Documentation), read on 17 September 2026.

The snippet to paste into Braze

Put it in html editor and drag-and-drop html component in braze. There are two shapes, and which one you want depends on whether every subscriber gets the same deadline or their own. The editor generates whichever you pick, already filled in with your ids.

One thing to know before you paste anything. A merge tag cannot go inside the image address. The address is signed, and the signature covers every part of it, so if Braze swaps a name into it after signing, the signature no longer matches and every subscriber gets the fallback graphic instead of your timer. It happens quietly, because the image service always answers with a valid picture rather than an error. Both snippets below put the merge tags in the link and the alt text, where they belong and where they work.

One image for the whole send

On every plan, including the free one. Everybody requests the same address, and the clock is worked out at the moment each person opens, which is what lets one address show a different time to every reader. The name and the code go in the link and the alt text.

<a href="https://yourstore.com/sale?code={{custom_attribute.${promo_code}}}" target="_blank" rel="noopener">
  <img src="https://img.emailtimer.app/i/k7mQ2pXd.gif?w=w_8ha3fq&v=3&sig=YOUR_SIGNATURE" alt="{{${first_name}}}, your 25% off ends at midnight tonight" width="600" height="200" border="0" style="display:block; width:100%; max-width:600px; height:auto; border:0;" />
</a>

A different image for each subscriber

On Growth and Agency. Generate one signed address per subscriber, import the column into Braze as a contact field called et_image_url, and point the snippet at that field. This is the only way a deadline that differs per subscriber can work, because the deadline is inside the signature too. A contact whose field is empty gets no image, so check the field is filled across the whole segment before you send.

<a href="https://yourstore.com/sale?code={{custom_attribute.${promo_code}}}" target="_blank" rel="noopener">
  <img src="{{custom_attribute.${et_image_url}}}" alt="{{${first_name}}}, your 25% off ends at midnight tonight" width="600" height="200" border="0" style="display:block; width:100%; max-width:600px; height:auto; border:0;" />
</a>

The addresses come from the batch endpoint in the REST API, or from a CSV export if you would rather not write code.

Setting it up in Braze

Four steps, and the longest one is deciding what the timer should look like.

  1. 1

    Design template in EmailTimer.App

    Configure your countdown style or multi-layer personalized image with custom fonts and layer rules.

  2. 2

    Copy Braze Liquid embed snippet

    The snippet puts Braze Liquid user attributes in the link and the alt text. A per-user image address goes in a custom attribute instead of a tag inside the address.

  3. 3

    Add to Braze campaign editor

    Paste the HTML code into a Braze HTML block or within your custom email template.

  4. 4

    Preview against user profiles

    Select random or specific test users in the Braze preview tab to verify rendered parameters.

Things that only matter in Braze

Braze utilizes Liquid syntax for user profile attributes and connected content. A value fetched with connected content cannot be added to a signed image address at send time. Put it in the link or the alt text, or sign one address per user through the API ahead of the send.

What your Braze subscribers will actually see

Gmail image proxy and Apple Mail privacy rules apply. Use fixed timestamps for flash sales and relative time offsets for transactional canvas workflows.

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.

Braze in practice

Braze countdown timer with Liquid, per user

By Danish Mohammed, founder. , updated 8 min read

A per-user countdown in Braze is an image whose address was signed for that user before the send and stored in a custom attribute. Liquid writes the attribute whole into the image source with {{custom_attribute.${et_image_url}}}, and personalizes the link and the alt text around it. Liquid never goes inside the address, because every parameter in it is signed.

Putting a timer into a Braze email is the easy half. The half that goes wrong is where the signed address gets made, and the right answer to that sits outside Braze. If you are weighing vendors for a whole programme rather than one Canvas, choosing a dynamic image and countdown timer vendor for an enterprise email programme is the longer read.

Does Braze have its own countdown timer

Not for email. Braze’s drag-and-drop editor blocks reference lists thirteen blocks for email, from Title to Product, and none of them is a timer.

Braze runs a partner directory, and its visual and interactive content pages send you to other image vendors for countdowns, some with “personalized expiration dates.” So a countdown in a Braze email is an image served from outside Braze, whoever serves it. Nothing here needs a Braze partner integration to be switched on: the timer is an ordinary image tag, and the only thing Braze stores is a custom attribute you already know how to write.

What Liquid personalizes, and what it must not

The signature decides it. An HMAC covers the path and every parameter in the image address, so a value Braze substitutes into it at send time leaves the signature pointing at text that is no longer there. Every user in the send then gets a grey “image unavailable” card, at HTTP 200, and nothing on either side logs an error you would see.

That leaves Liquid three jobs: the link, the alt text, and handing over one complete address untouched.

Diagram of the timer snippet in Braze Liquid. The link, carrying a promo code custom attribute, and the alt text, with a first name and a default, are marked as safe to personalize. The image source is a single custom attribute holding a complete signed address, marked whole only: never a value spliced into part of it.
The three slots in the snippet, and which of them Liquid may fill.

Braze writes a standard attribute as {{${first_name}}}. A custom attribute needs a namespace: to use one, the Liquid guide says, “you must add the namespace” custom_attribute to the variable. An attribute called et_image_url is therefore {{custom_attribute.${et_image_url}}}.

<a href="https://yourstore.com/sale?code={{custom_attribute.${promo_code}}}" target="_blank" rel="noopener">
  <img src="{{custom_attribute.${et_image_url}}}" alt="{{${first_name} | default: 'Hi'}}, your offer ends soon" width="600" height="200" border="0" style="display:block; width:100%; max-width:600px; height:auto; border:0;" />
</a>

Braze’s own fallback example is {{ ${first_name} | default: 'Valued User' }}. Its single quotes are safe inside a double-quoted alt, where a double-quoted default would close the attribute early. Keep them straight, too: Braze warns about curly quotes pasted from other editors, “Use straight quotes (’ ’) in your Liquid in Braze.”

The one thing Liquid must not do is build the address itself: a base address plus &deadline={{custom_attribute.${trial_end}}}. It is the shape people reach for first, and against a signed address it fails for the whole send.

Where a name inside the picture comes from

If the image itself should say the user’s name, that value is not Liquid’s either. It travels with the signed address: a recipient token in the address, and the name stored with the recipient list, read by the image service at the open.

Countdown headed Happy birthday, Priya, reading 02 days 11 hours 59 minutes The same design headed Happy birthday, Tom, reading 07 days 11 hours 59 minutes
One gallery design, two users. The name and the deadline are drawn by the product's engine from each user's own row, rendered at 12:00 UTC on 23 September 2026. Braze only carried the address.

Getting a signed address onto every user

The per-user addresses come from a recipient list upload or the REST API’s batch endpoint, and Growth is the first plan with either.

For an upload, put the Braze external_id in the file’s external_id column, the user’s deadline in deadline as a full timestamp with a zone, and any values to draw in, such as first_name. A file takes up to 500,000 rows. The download gives one signed address per row beside the same external id, which is the key you need to write the addresses back to Braze as a custom attribute, by whatever route you already use for attributes.

Five-step diagram. Your data holds a deadline and a key per user. EmailTimer.App signs one address per user before the send, from a recipient list upload or the batch API. The address is stored in a custom attribute on the user profile, such as et_image_url. At the send, Liquid writes the attribute whole into src, with no Connected Content call. At the open, the image service reads the token and draws that person's deadline. A warning below says Braze must not write a value into part of the address.
The deadline is fixed in step 2. Braze carries the address and renders nothing about the timer itself.

In the dashboard, choose Braze on the publish screen and pick a different image for each recipient. The snippet comes out in the Braze form, with the custom attribute already namespaced.

The Snippet tab of the publish dialog with Braze chosen and A different image for each recipient selected. The code block shows an image tag whose src is the custom_attribute et_image_url Liquid tag and a link carrying the custom_attribute promo_code tag
The publish dialog's Braze snippet, per recipient, captured from a local test instance of the dashboard.

Where the snippet goes

Use the HTML block in the drag-and-drop editor. Braze describes it as the block that “Inserts raw HTML. Recommended for Liquid, such as Connected Content or conditional statements.” The personalization note on the same page is blunter: “Note that Liquid isn’t supported in image blocks or in button URL fields.” The Image block entry on that page also mentions dynamic images with Liquid, so the page disagrees with itself. The HTML block is the placement it recommends without a caveat.

Outside drag-and-drop, Braze’s Liquid guide says to use the HTML editor rather than the classic editor, which “may parse the Liquid as plaintext”, and to keep Liquid inside the body tag.

For one deadline shared by the whole send, paste the campaign snippet with its fixed address. That is on the free plan, and readers still see different numbers, since the time left is drawn when each mail client asks for the image.

Connected Content and minting at send time

Connected Content can call an API while Braze renders each message, so it looks like the obvious way to fetch a signed address per user. Braze’s own reference is the best argument against it.

Braze does not throttle it. The Connected Content reference says “Braze does not apply a separate rate limit to Connected Content”. Call volume, it says, “scales directly with your message delivery speed rate limit.” One recipient can also cost several calls, because Braze renders the HTML body, the plain text body and any AMP version separately.

The API here does have limits. Minting one URL per request allows 60 requests a minute per key. The batch route allows 600 a minute, at up to 10,000 URLs a call. A campaign delivering thousands of emails a minute through single-URL calls would start collecting 429s within the first minute.

In Braze, a 429 costs more than one missing image. It is one of the status codes counted toward unhealthy host detection, and when that trips, “Braze temporarily halts requests to the target host for one minute”. Slowness fails too: “if the server takes longer than two seconds to respond, the content is not inserted.” Either way those users get an empty src.

So mint before the send. One batch call covers 10,000 users while Braze is not yet involved, and the send stops depending on a third server answering inside two seconds.

Canvas, evergreen timers and send time

In a Canvas, write the attribute well before the user reaches the message step. If the write and the step happen together, some users can render before their address exists. Check the attribute is filled across the whole segment before the send, since a user with no address gets no image.

An evergreen timer that starts at first open is tracked per signed address. A campaign address is therefore one clock for the whole send, started by whoever opens first. A clock per user needs an address per user, which puts you back on the Growth route above. The evergreen-from-send mode carries the send instant inside the signed address, so that value is fixed when the address is minted rather than filled in by Braze. Email countdown timer types sets out all six modes.

What your recipients will see

Once Braze hands the message over, the mail client decides what the image does. In Outlook with animation switched off, Microsoft’s support page says, “If only the first frame of the animation appears, you have animations, or animated GIFs, disabled.” The first frame of every GIF rendered here carries the real time left, so that reader sees a correct number that does not move.

Apple’s Protect Mail Activity “downloads remote content in the background by default”, so the number an Apple Mail reader sees can reflect delivery rather than the open. Google’s Workspace documentation says “Gmail uses Google’s secure proxy servers to serve images”, and a proxy can answer a later open from its own copy. The email client support page goes through each client. All of that comes from the vendors’ documentation. I have not yet watched one of these timers arrive in a real inbox.

Test before you send

Braze’s test message guide has the tool that matters here: preview as a custom user. Braze then lets you “enter text for various fields available for personalization, such as the user’s first name and any custom attributes.” Paste a real signed address into the image attribute’s field and check the preview.

Then send it. Select the Test Send tab, add your address in Add individual users, and select Send Test. Open the result in two different mail apps, because a preview only shows that the Liquid resolved, and the fetch by a real mail client is the part being tested.

Questions and answers

How do I personalize a Braze countdown timer with Liquid?

Put Liquid in the link and the alt text, and make the image src one custom attribute holding a complete signed address, {{custom_attribute.${et_image_url}}}. The deadline and any name drawn in the picture are fixed when that address is signed, before the send.

Can I put a Liquid attribute inside the timer image URL?

Not inside a signed address. Braze would substitute the value after signing, and every user would get the grey “image unavailable” card.

Can Connected Content fetch a signed URL for each user at send time?

It can make the call, but Braze applies no separate rate limit to it, and the single-URL route here allows 60 requests a minute per key. Mint the addresses beforehand with the batch route, which takes 10,000 per call.

Why does my timer image break in the drag-and-drop editor?

One documented cause is Liquid in an Image block. Braze’s editor reference says Liquid isn’t supported in image blocks, so move the tag into an HTML block.

Do I need a paid plan for a Braze countdown timer?

Not for one deadline shared by the send, which is on the free plan with 5,000 image opens a month. An address per user needs the API or a recipient list upload, which start at Growth for $49 a month. The tiers are on pricing.

Sources

Related

Campaigns to build in Braze

Each playbook says which timing model the campaign needs, which snippet it takes, and what to get right before you send it.

Setting this up on another platform

The snippet is the same everywhere. Only the merge syntax changes, and each page below carries the exact syntax that platform expects.

All 21 platform setup guides

Put a timer in your next Braze send

Free for 5,000 opens a month, $19 for 250,000, and the snippet above is the whole integration.