REST API + MCP server

The countdown timer
API for developers

Create, list, update, archive and delete countdown timers programmatically. Every API key is scoped to your organization — timers you manage via the API show up right alongside the ones you make in the dashboard.

Authentication

Create a key from Settings → API Tokens. The plaintext value is only ever shown once, right after creation — store it somewhere safe. Pass it as a bearer token on every request via the Authorization header:

Authorization: Bearer ft_live_4f9c2a1b...

Revoking a key from the dashboard takes effect immediately — requests using a revoked key get a 401.

Every endpoint below is relative to https://api.flashtimer.com, and described in its OpenAPI document. API access is included on the Pro plan and above.

Create a timer

POST/v1/timers

name and expiry_datetime are the only required fields — everything else, including the entire visual theme, falls back to sane defaults. A successful response returns the new timer's id:

{
  "id": "5da851e7-f41d-4af8-bd87-f45611a6bd67"
}

For all available fields and a full theme example, see Field reference below.

List timers

GET/v1/timers

Returns timers belonging to your organization, newest first by default. All parameters are optional query-string parameters.

[
  {
    "id": "5da851e7-f41d-4af8-bd87-f45611a6bd67",
    "name": "Black Friday Sale",
    "description": "",
    "end_date_time": "2026-11-27T23:59:00-05:00[America/New_York]",
    "is_archived": false,
    "created_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-02T00:00:00.000Z"
  }
]

An evergreen, dynamic, recurring, cycle or count-up timer has no single end date (the first two end per recipient, the others never end), so its end_date_time is the placeholder 2099-01-01T00:00:00[UTC]. Retrieve the timer for its schedule.

Query parameters

ParameterTypeDescription
querystringCase-insensitive substring match on name.
status"all" | "active" | "expired" (repeatable)Defaults to all statuses.
archivedbooleanList archived timers instead of active ones. Defaults to false.
order_by"name" | "created_at" | "updated_at" | "end_date_time"Defaults to created_at.
order_direction"asc" | "desc"Defaults to desc.
limitinteger (1-100)Defaults to 100.
offsetintegerDefaults to 0.

Retrieve a timer

GET/v1/timers/:id

Returns a timer's full configuration, including its theme — the same shape you'd send to create or update it.

{
  "name": "Black Friday Sale",
  "description": "",
  "organization_id": "org_2abc123def456",
  "expiry_datetime": "2026-11-27T23:59:00-05:00[America/New_York]",
  "expiry_message": "Sale has ended!",
  "language": "en",
  "is_archived": false,
  "theme": { "...": "same shape as the theme you sent on create" }
}

Update a timer

PATCH/v1/timers/:id

Partial update — send only the fields you want to change. Anything you omit, including nested theme fields, is left exactly as it was — with one exception: the timer has one way of ending, so setting one of evergreen_duration, recurrence, cycle, count_up or dynamic replaces whichever of them the timer had, and drops start_on_send unless the new one is an evergreen_duration. The response is the full timer config after the update (for a token with only the timers:write scope, just { "id": "…", "updated": true }):

{
  "name": "Extended Black Friday Sale",
  "description": "",
  "organization_id": "org_2abc123def456",
  "expiry_datetime": "2026-11-29T23:59:00-05:00[America/New_York]",
  "expiry_message": "Sale has ended!",
  "language": "en",
  "is_archived": false,
  "theme": { "...": "unchanged fields are left as they were" }
}

Delete a timer

DELETE/v1/timers/:id

Permanently deletes the timer. There's no undo — if you might want it back, use Archive instead.

A successful response is 204 No Content with an empty body.

Archive & restore a timer

Archiving hides a timer from your active list without deleting it — useful for sales that ended but you still want to reference later.

POST/v1/timers/:id/archive

{ "archived": true }

Restore

POST/v1/timers/:id/restore

{ "restored": true }

Dynamic timers

A dynamic timer (dynamic with a timezone and an optional default_time) counts down to a different date for each recipient: the one your email platform puts in the image link's end parameter, usually with a merge tag for a field such as their trial end. Create it without an expiry_datetime and embed the GIF URL with ?end= followed by the date field's merge tag. It needs the Pro plan or higher, and a timer a store integration manages can't be dynamic.

The value is read in these formats. An end with no zone is read in the timer's timezone, and a date alone ends at default_time there.

FormatExample
Date only, at default_time in the timer's timezone2026-10-05
Date and time in the timer's timezone2026-10-05T18:00, 2026-10-05 18:00:30 or 2026-10-05 18:00:00.000
ISO 8601 with a zone (T or a space before the time)2026-10-05T18:00:00+03:00, 2026-10-05 18:00:00 +0300 or 2026-10-05T15:00:00Z
Date and time followed by UTC or GMT, as Liquid prints a UTC time2026-10-05 15:00:00 UTC
Unix seconds (10 digits)1791212400
Unix milliseconds (13 digits)1791212400000
Slash date, optionally with a time. Without endfmt, only when the day can't be mistaken for the month; add &endfmt=mdy or &endfmt=dmy to read any slash date month-first or day-first10/25/2026, 25/10/2026 18:00 or 10/05/2026 with &endfmt=mdy

A missing end shows the timer's expired message and fallback image, as does a date in the past. An unreadable one shows a neutral “currently unavailable” image instead, which isn't cached, and an end more than ten years away is unreadable. Your platform must output the date in one of the formats above; a literal + in an offset works even when it isn't URL-encoded.

An explicit midnight means midnight: a platform that prints a date field as 2026-10-05 00:00:00, or stores it as a midnight-UTC timestamp, ends the countdown at the start of that day. Have it output the date alone (2026-10-05) to end at default_time. Dates written with slashes (10/05/2026) are refused, since month and day order varies.

The end value is not signed: anyone holding the link can change it, which only changes their own countdown. Don't use a dynamic timer for anything that has to be enforced; use a send link for a deadline recipients can't change.

curl -X POST "https://api.flashtimer.com/v1/timers" \
  -H "Authorization: Bearer ft_live_4f9c2a1b..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Trial ending", "dynamic": { "timezone": "Europe/Athens", "default_time": "18:00" } }'

# Embed, with your email platform's merge tag for the date field (Klaviyo shown):
# https://timers.flashtimer.com/xK3mZ9pQvT.gif?end={{ person.trial_end }}
# Each recipient's link then reads like:
# https://timers.flashtimer.com/xK3mZ9pQvT.gif?end=2026-10-05

Field reference

These are the body fields accepted by Create. Update accepts the exact same fields, all optional — only the ones you include are changed, except that setting one way of ending (evergreen_duration, recurrence, cycle, count_up or dynamic) replaces the one the timer had (see Update a timer).

FieldTypeRequiredDescription
namestringYesThe timer's name (1-200 characters).
expiry_datetimestringNoZoned datetime the timer counts down to, e.g. 2026-12-31T23:59:00+02:00[Europe/Athens]. Required unless evergreen_duration, recurrence, cycle, count_up or dynamic is set, and refused alongside one.
descriptionstringNoInternal note about the timer (not shown to recipients).
expiry_messagestringNoMessage shown once the timer has expired.
languagestringNoLabel language: "auto" or one of en, fr, es, de, it, pt, nl. A region is dropped ("fr-CA" is stored as "fr"); anything else is refused. "auto" uses each reader's browser language, which Gmail's and Apple's image proxies hide. A lang= parameter on the image URL, such as your email platform's language merge tag, overrides the timer's language per send. Defaults to "en".
fallback_image_keystringNoKey of the static image shown after expiry. Upload the image to the timer in the dashboard (Timing tab, Fallback image); GET /v1/timers/:id then returns its key. A key that isn't one of your organization's uploads is refused with 422. Requires the Basic plan or higher.
evergreen_durationobjectNoCounts down this long instead of to a date, e.g. { "amount": 48, "unit": "hours" } (units: minutes, hours, days, weeks; amount up to 999). By default the countdown starts at each recipient's first open. Leave expiry_datetime out; the timer's stored expiry_datetime is then 2099-01-01T00:00:00[UTC], a placeholder that is never rendered. On Update, null switches back to a fixed deadline and needs an expiry_datetime in the same request.
start_on_sendbooleanNoWith evergreen_duration, counts from the send instead of the first open; refused without one. Embed these timers with a send link (see Send links); their plain URL shows the expired message.
recurrenceobjectNoResets at that time instead of expiring: { "repeat": "daily" | "weekly" | "monthly", "time": "HH:MM", "weekday"?: 0-6 (0 = Monday, required for weekly), "day_of_month"?: 1-31 (required for monthly; clamped to the month's last day), "timezone": IANA name, e.g. Europe/Athens }. A field the repeat doesn't use is ignored; an unknown key is refused. Leave expiry_datetime out. Requires the Basic plan or higher, and is refused on a timer a store integration manages. On Update, null switches back to a fixed deadline and needs an expiry_datetime in the same request.
cycleobjectNoLoops forever from the timer's creation: { "period_minutes": 1-1438560 }. Leave expiry_datetime out. The cycle.anchor in responses is read-only; an anchor sent in a request is ignored. Requires the Basic plan or higher, and is refused on a timer a store integration manages. On Update, null switches back to a fixed deadline and needs an expiry_datetime in the same request. Only one of evergreen_duration, recurrence, cycle, count_up and dynamic can be set.
count_upobjectNoCounts up from a start time instead of down to one: { "from": zoned datetime, e.g. 2026-10-05T09:00:00+03:00[Europe/Athens] }. Shows 00:00:00 until then and never stops. Leave expiry_datetime out; start_on_send is refused with it. Requires the Basic plan or higher, and is refused on a timer a store integration manages. On Update, null switches back to a fixed deadline and needs an expiry_datetime in the same request. Only one of evergreen_duration, recurrence, cycle, count_up and dynamic can be set.
dynamicobjectNoCounts down to an end date each recipient's link carries in its ?end= URL parameter: { "timezone": IANA name such as Europe/Athens (not an offset), "default_time": "HH:MM", default "23:59" }. Leave expiry_datetime out; start_on_send is refused with it. See Dynamic timers for the accepted ?end= formats. Requires the Pro plan, and is refused on a timer a store integration manages or one in an A/B test (an A/B test's embed carries no recipient's date). On Update, send timezone each time; a default_time left out keeps the timer's current one ("23:59" for a timer switching to dynamic), and null switches back to a fixed deadline and needs an expiry_datetime in the same request. Only one of evergreen_duration, recurrence, cycle, count_up and dynamic can be set.
themeobjectNoVisual overrides — see below. Any field you omit falls back to the default theme.

theme fields

All optional. Unknown keys are rejected with a 422 so typos don't silently fall back to defaults.

FieldTypeDescription
shape_type"ft_01" | "ft_02" | "ft_03" | "ft_04" | "ft_05" | "ft_06"Which of the six timer visual templates to use.
font_size"small" | "medium" | "large"Digit font size.
fontstringA font id, not a name: one of the system fonts or a font your organization uploaded. It's the last part of the font's address under Fonts (/fonts/<id>). An id that matches no such font is refused with 422. Choosing a font your organization uploaded needs the Basic plan or higher; without it the request returns 403 with code plan_feature_required.
backdrop_fillhex colorBackground fill color, e.g. #0F172A.
no_backdropbooleanMake the background transparent instead of filling it with backdrop_fill. A GIF pixel is either transparent or solid, so the timer's antialiased edges are blended into matte_color. Requires the Basic plan or higher; turning it on without one returns 403 with code plan_feature_required.
matte_colorhex colorWith no_backdrop, the colour the timer's edges are blended into. Set it to the email background behind the timer, or the edges show a fringe of this colour. Defaults to #FFFFFF.
message_colorhex colorColour of expiry_message's text once the timer has ended. Leave it out to use digits.color.
digits.leading_zerobooleanPad single digits with a leading zero.
digits.colorhex colorDigit text color.
digits.fillhex colorDigit background fill color.
digits.border_colorhex colorOptional digit border color.
labels.colorhex colorColor of the unit labels (days/hours/etc).
labels.displaybooleanWhether unit labels are shown.
separators.colorhex colorColor of the separators between units.
separators.displaybooleanWhether separators are shown.
days.displaybooleanWhether the days unit is shown.

Responses

Error responses you may get from any /v1/timers endpoint, with an example body for each. Toggle Show Schema to see the shape instead of example values.

{
  "error": "expiry_datetime must be a valid zoned datetime string"
}
A fixed timer's expiry_datetime isn't a parseable zoned datetime string.

Rate limits

Requests are limited per organization (not per key — all keys belonging to the same organization share the same limit) by plan: 60 per minute on Pro, 150 per minute on Business and 300 per minute on Enterprise. The Free and Basic plans don't include API access. Once you hit the limit, you'll get a 429 until the window resets.

MCP server

flashtimer also exposes an MCP server at https://mcp.flashtimer.com/mcp, so AI assistants like Claude and ChatGPT can create and manage your timers directly. It's available on the Pro plan and up, with the same rate limit as the REST API.

Setting it up for a marketing team? The guide to flashtimer in AI assistants has the steps for Claude and ChatGPT, example requests and troubleshooting.

Connect from Claude

  1. In Claude, open Customize → Connectors and choose Add custom connector.
  2. Enter https://mcp.flashtimer.com/mcp and select Add.
  3. Sign in to flashtimer, choose the organization and select Allow.

Claude acts as you, in the flashtimer organization you choose. Disconnect it at any time in Settings → Connected Apps. On Claude Team or Enterprise, an Owner of your Claude organization adds the connector first under Claude's Organization settings → Connectors (Add, then Custom); each member then selects Connect under Customize → Connectors.

In Claude Code, add it with this command, then run /mcp in Claude Code and follow the steps in your browser to sign in:

claude mcp add --transport http flashtimer https://mcp.flashtimer.com/mcp

Connect from ChatGPT

  1. In ChatGPT on the web, open Plugins, select +, then Add custom MCP server (your account's and workspace's settings decide whether you can).
  2. Enter a name and the URL https://mcp.flashtimer.com/mcp, choose OAuth authentication, confirm the warning and select Create as a plugin.
  3. Install it, then sign in to flashtimer, choose the organization and select Allow, as with Claude.

Use it from the OpenAI API

The OpenAI Responses API calls the server itself: pass an API token as the authorization of an mcp tool. OpenAI doesn't store it, so send it with every request.

curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5",
    "tools": [
      {
        "type": "mcp",
        "server_label": "flashtimer",
        "server_url": "https://mcp.flashtimer.com/mcp",
        "authorization": "ft_live_4f9c2a1b...",
        "require_approval": "always"
      }
    ],
    "input": "List my flashtimer timers"
  }'

Use an API token

Clients that take a fixed header, like Cursor or VS Code, can use an API token instead: pass it as a bearer token in the Authorization header of your client's MCP config. A token with only the read scope gets the read tools.

{
  "mcpServers": {
    "flashtimer": {
      "url": "https://mcp.flashtimer.com/mcp",
      "headers": {
        "Authorization": "Bearer ft_live_4f9c2a1b..."
      }
    }
  }
}

Example prompts

Once it's connected, ask in your own words. For example:

  • “Create a countdown timer for our weekend sale that ends Sunday at midnight New York time, in our brand colours.”
  • “Make the Spring Sale timer red and push its end back by two days.”
  • “Create a timer from my store's discount SUMMER20 and give me the HTML for my email.”
  • “How did my timers do over the last 30 days, and which one got the most clicks?”
  • “Put the Flash Sale timer at the top of my Klaviyo campaign Weekend Launch.”

Available tools

Tools that create, revise or fetch a timer also return its image URL, a paste-ready HTML snippet for your email and a preview image, so the assistant can hand you the code and see what it made.

In Claude and ChatGPT the result also appears as a card: the timer running, with its texts, colours, end date, font and shape to change and preview before you save, and a button that copies the email snippet.

An assistant can read a timer linked to a store discount but not change, archive or delete it (it can still set or clear the timer's stock signal); it says where to change it instead. A timer your store syncs follows the discount on its own; one you linked by hand takes on new dates when it's resynced.

A/B tests and stock signals need the Business plan: starting or resuming a test, adding a click destination, listing store products and setting a stock signal. Reading a test, pausing it, promoting its winner and clearing a signal work on Pro too.

ToolAccessDescription
create_timerWriteCreates a new timer. Same fields as POST /v1/timers.
list_timersReadLists your timers, with optional filtering, ordering, and pagination.
get_timerReadFetches the full configuration of a single timer by id.
archive_timerWriteArchives a timer so it stops showing as active.
restore_timerWriteRestores a previously archived timer.
create_send_linkWriteMints the embed URL for one send of a start-on-send timer. Same as POST /v1/timers/:id/send-links.
design_timerWriteDesigns a new timer's settings from a free-text brief, e.g. "20% off, ends Friday, dark theme". It picks settings; it doesn't create images.
revise_timerWriteChanges an existing timer's settings (texts, colours, layout, or end date) from a free-text instruction, e.g. "make it red and extend by 2 days". A recurring, cycle, count-up, evergreen or dynamic timer has no shared end date, so it keeps its schedule: the result's expiry_datetime is null, with a note when a new end date was asked for.
update_timerWriteChanges exactly the fields you send on a timer. Same fields as PATCH /v1/timers/:id.
duplicate_timerWriteCopies a timer into a new one named "<name> (Copy)".
delete_timerWriteDeletes a timer for good. Prefer archive_timer to just hide it.
list_promotionsReadLists the active and upcoming discounts of your connected stores (Shopify, WooCommerce, BigCommerce, PrestaShop, Magento, Squarespace).
create_timer_from_promotionWriteCreates a timer for a store discount, in your brand kit's style, linked to it (resync it when the discount's dates change).
link_timer_to_promotionWriteLinks an existing timer to a store discount; the timer takes on its dates.
resync_timer_to_promotionWriteGives a timer linked to a store discount by hand that discount's current dates, like Resync in the app.
get_brand_kitReadYour brand kit's colours and font, which new timers start in.
list_fontsReadThe fonts a timer can use, by name, with the id to pass as theme.font.
get_usageReadThis billing period's timer views against your plan's limit plus any top-up credit, and whether timers have gone static.
get_analyticsReadViews, uniques, clicks, top timers, countries, email clients and devices for a range.
list_stock_productsReadA connected store's products and stock.
set_inventory_signalWriteExpires a timer when a product's stock reaches a threshold (confirm first if it already has).
clear_inventory_signalWriteStops watching stock.
list_ab_testsReadYour A/B tests.
get_ab_testReadA test's results, embed snippet and tracked links.
create_ab_testWriteStarts an A/B test between two timers.
set_ab_test_activeWritePauses or resumes a test.
promote_ab_test_winnerWriteEnds a test and serves the winner to everyone (confirm first).
add_ab_click_destinationWriteAllows a URL for the test's tracked link.
list_campaignsReadCampaigns on your connected email platforms.
preview_campaign_embedWriteShows what putting a timer into a campaign will do, with a confirmation token.
embed_timer_in_campaignWriteWrites the timer into the campaign, with the token.

Troubleshooting

Connecting says the organization needs the Pro plan
Connecting an AI assistant is available on the Pro plan and up. On the page that asks you to allow the connection, switch the Organization to one on Pro or higher.
The assistant can only read
It has only the access it was given: an app that asked only to read gets only the read tools. Disconnect the app and connect it again.
It says the token or connection needs read access
An assistant has to be able to see your timers. Use an API token with the read scope as well as write. An app that asks only to change timers is turned away on the page where you allow the connection, with only Cancel to choose: it has to ask for read access too.
It says it can't change a timer
An assistant can't change, archive or delete a timer linked to a store discount; setting or clearing its stock signal still works. Change the discount in your store, or open the timer in flashtimer. A timer you linked by hand can be resynced to the discount's current dates.
New tools don't show up
Assistants load the tools when they connect. Start a new conversation; if they're still missing, disconnect and connect again. In ChatGPT, open it under Plugins and select Refresh.
New tools don't show up in a plugin from ChatGPT's directory
It gets new tools once OpenAI has reviewed them.
Disconnecting an assistant
Disconnect it in the assistant, or revoke any connection in flashtimer under Settings → Connected Apps. A connection left unused for 30 days expires on its own.
Requests are refused for a while
The MCP server shares the REST API's rate limit. Wait a moment and try again.

Still stuck? Contact support.

Expiry webhooks

Add one or more webhook URLs to a timer and flashtimer will POST a JSON payload to each URL once that timer's countdown reaches zero — usually within five minutes, later when many timers expire at the same time. Use this to trigger Zapier Zaps, Make scenarios, or any custom automation endpoint — without writing any server-side code.

How to add a webhook

  1. Open the timer editor and click Actions in the left panel.
  2. Click On Expiry to open the webhook dialog.
  3. Paste your webhook URL into the input field and click + Add. You can add multiple URLs — each one will be called in turn.
  4. Click Save. The webhooks are stored immediately; no publish step is required.

Payload

flashtimer sends this JSON body with Content-Type: application/json:

{
  "event": "timer.expired",
  "timer": {
    "id": "5da851e7-f41d-4af8-bd87-f45611a6bd67",
    "name": "Black Friday Sale",
    "expiredAt": "2026-11-27T23:59:00.000Z",
    "organizationId": "org_01jk..."
  },
  "timestamp": "2026-11-27T23:59:01.234Z"
}

Connecting to Zapier or Make

Both platforms expose a "catch webhook" trigger URL you can paste directly into the dialog:

  • Zapier — create a Zap, choose Webhooks by Zapier → Catch Hook, copy the URL, paste it here. See the Zapier integration guide for the full walkthrough.
  • Make — add a Webhooks → Custom webhook module, copy the URL, paste it here. See the Make integration guide.

Notes

  • Webhooks fire at most once per expiry. Once a timer has expired and its webhooks have been called, they fire again only if you re-arm the timer: save its webhook list again, or move its end date into the future, and they fire at the next expiry.
  • A timer's URLs are called one after another, each given five seconds to answer, and each is called even if one before it fails or times out. A URL that points at a private or internal address is refused when you save it, and skipped if one is ever stored. A failed call is not retried, so use an endpoint that is reliably up, such as a Zapier or Make webhook.
  • Only a timer with a fixed end date fires. Evergreen, dynamic, recurring, cycle and count-up timers have no single moment when they end, and an archived timer doesn't fire.
  • Requests aren't signed. If your endpoint acts on them, give it a URL that is hard to guess and check that the timer id in the payload is one of yours.
  • The dialog shows a green Last fired timestamp once the webhooks have been called, so you can confirm delivery without leaving flashtimer.

Klaviyo

Connect Klaviyo so flashtimer can list your draft and scheduled campaigns inside the Deploy → Send to ESP panel. Pick a campaign and click Embed Timer: flashtimer adds the timer to the campaign for you, with a preview first — no tab-switching needed.

Setup

  1. In Klaviyo go to Account → Settings → API Keys and click Create Private API Key.
  2. Name it (e.g. flashtimer) and copy the key — it starts with pk_. Klaviyo shows it only once.
  3. Paste the key under Settings → Integrations → Email Platforms → Klaviyo.

What it unlocks

  • Draft and scheduled campaigns are fetched and shown in the Send to ESP panel.
  • flashtimer generates a ready-to-paste <img> snippet pointing at the live timer GIF on timers.flashtimer.com.

Or paste it into Klaviyo yourself

Add a Custom HTML block in your email and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;width:100%;" />

Mailchimp

Connect Mailchimp to pull draft campaigns into the Send to ESP panel and generate a countdown embed in seconds.

Setup

  1. In Mailchimp go to Profile → Extras → API Keys → Generate Key and copy the key.
  2. Find your server prefix — the part before .api.mailchimp.com in your API endpoint URL, e.g. us1.
  3. Enter both under Settings → Integrations → Mailchimp.

What it unlocks

  • Campaigns with status save (draft) are listed in the Send to ESP panel.
  • Embed Timer adds the timer to the selected campaign in one click.

Or paste it into Mailchimp yourself

Add a Code block in your campaign and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;width:100%;" />

Omnisend

Omnisend works by pasting the timer into your email: there's nothing to connect. Open the timer, click Embed and copy its HTML. For an evergreen or dynamic timer, choose Omnisend as the email platform there, so the URL carries Omnisend's tag, such as [[contact.email]].

Paste it into Omnisend

Add an HTML block in your campaign and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;max-width:100%;" />

ActiveCampaign

Connect ActiveCampaign to see draft and scheduled email campaigns and embed countdown timers — useful for time-sensitive automation sequences.

Setup

  1. In ActiveCampaign go to Settings → Developer. Both your API URL (e.g. https://youraccount.api-us1.com) and your API Key are on that page.
  2. Copy both values.
  3. Enter them under Settings → Integrations → ActiveCampaign.

What it unlocks

  • Draft and scheduled campaigns (status 0 and 1) are listed in the Send to ESP panel.
  • Embed Timer adds the timer to the selected campaign in one click.

Paste into ActiveCampaign

Add an HTML block in your campaign and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;max-width:100%;" />

Drip

Connect Drip to embed countdown timers in your broadcasts and workflows. Requires your API token and your numeric Account ID.

Setup

  1. In Drip go to Settings → User Settings and copy your API Token.
  2. Find your Account ID — it's the number in the URL when logged in: app.getdrip.com/{accountId}/....
  3. Enter both under Settings → Integrations → Drip.

Or paste it into Drip yourself

Add a Custom HTML block in your broadcast and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;max-width:100%;" />

Brevo

Connect Brevo (formerly Sendinblue) to pull draft and scheduled email campaigns into flashtimer and embed countdown timers with one click.

Setup

  1. In Brevo go to Profile → SMTP & API → API Keys and click Generate a new API key.
  2. Copy the key — it starts with xkeysib-.
  3. Paste it under Settings → Integrations → Brevo.

What it unlocks

  • Draft and scheduled email campaigns are fetched and shown in the Send to ESP panel.
  • Embed Timer adds the timer to the selected campaign in one click.

Or paste it into Brevo yourself

Add a Raw HTML block in your campaign and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;max-width:100%;" />

Wix

Connect Wix to pull your draft Email Marketing campaigns into flashtimer and add a countdown timer to one made in Wix's HTML email editor in one click. Requires a Wix API key and your Site ID.

Setup

  1. In your Wix dashboard go to Settings → API Keys and click Generate API Key.
  2. Name it (e.g. flashtimer) and copy the key.
  3. Find your Site ID — it appears in your site dashboard URL: manage.wix.com/{siteId}/....
  4. Enter both under Settings → Integrations → Wix.

What it unlocks

  • Draft Email Marketing campaigns are listed in the Send to ESP panel.
  • Embed Timer adds the timer to the selected campaign in one click, if it was made in Wix's HTML email editor. For any other, paste the timer yourself (below).

Or paste it into Wix yourself

In Wix's campaign editor, add an HTML element and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;max-width:100%;" />

Constant Contact

Constant Contact works by pasting the timer into your email: there's nothing to connect. Open the timer, click Embed and copy its image URL or HTML. For an evergreen timer, choose Constant Contact as the email platform there, so the URL carries [[emailAddress]], and paste the HTML into a custom code email: Constant Contact doesn't always fill in the tag in an image URL. Test with two contacts: each should see their own countdown.

Paste into Constant Contact

In an email built in the drag-and-drop editor, add an Image block, choose Image URL and paste the timer's image URL, such as https://timers.flashtimer.com/xK3mZ9pQvT.gif. Paste the address rather than uploading the GIF: an uploaded copy stops counting down.

In a custom code email, paste this where the timer belongs:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;max-width:100%;" />

GetResponse

Connect GetResponse to pull draft newsletters into flashtimer and embed countdown timers without leaving the timer editor.

Setup

  1. In GetResponse go to Profile → Integrations & API → API.
  2. Click Generate API Key and copy the key.
  3. Paste it under Settings → Integrations → GetResponse.

What it unlocks

  • Draft newsletters (type draft) are listed in the Send to ESP panel.
  • Embed Timer adds the timer to the selected newsletter in one click.

Or paste it into GetResponse yourself

Add a Custom HTML block in your newsletter and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;max-width:100%;" />

MailerLite

Connect MailerLite to pull draft email campaigns into flashtimer and embed countdown timers in one click. Requires a MailerLite Advanced plan for API access.

Setup

  1. In MailerLite go to Integrations → Developer API.
  2. Click Generate new token, name it, and copy it.
  3. Paste it under Settings → Integrations → MailerLite.

What it unlocks

  • Draft email campaigns are fetched and shown in the Send to ESP panel.
  • Embed Timer adds the timer to the selected campaign in one click.
  • Requires an Advanced plan — API access is not available on the Free plan.

Or paste it into MailerLite yourself

Add an HTML block in your campaign and paste:

<img src="https://timers.flashtimer.com/xK3mZ9pQvT.gif"
     alt="Black Friday Sale"
     style="display:block;outline:none;border:none;text-decoration:none;max-width:100%;" />

Zapier

Connect Zapier to send timer events to any of Zapier's 7,000+ supported apps — Slack, Notion, Google Sheets, Airtable, Salesforce, and more. No code required. flashtimer POSTs a JSON payload to your Zap the moment you click Send timer data in a timer's Send to ESP panel.

Setup

  1. In Zapier create a new Zap and choose Webhooks by Zapier as the trigger.
  2. Select Catch Hook and click Continue.
  3. Copy the unique webhook URL (e.g. https://hooks.zapier.com/hooks/catch/...).
  4. Paste it under Settings → Integrations → Zapier.
  5. Add an action step in your Zap (send Slack message, log to Sheets, tag a CRM contact, etc.) and turn it on.

What it unlocks

  • flashtimer POSTs a structured JSON payload every time you send a timer to a platform.
  • Build multi-step Zaps: notify a Slack channel, update a Notion database, create a CRM task — all from one timer send.
  • The Zapier panel in the timer editor shows whether the last send succeeded or failed, so you can troubleshoot Zap filters without leaving flashtimer.

Webhook payload

flashtimer sends this JSON to your Zap when you click Send timer data:

{
  "timerName": "Black Friday Sale",
  "timerUrl": "https://timers.flashtimer.com/xK3mZ9pQvT.gif",
  "gifUrl": "https://timers.flashtimer.com/xK3mZ9pQvT.gif",
  "expiresAt": "2026-11-27T23:59:00-05:00[America/New_York]"
}

timerUrl and gifUrl point at the live GIF — embed either directly in a Slack message or HTML email action. expiresAt is null for an evergreen, recurring, cycle, count-up or dynamic timer, which has no single end date. (Evergreen timers used to send the placeholder 2099-01-01T00:00:00[UTC] here.)

Shopify

Connect your Shopify store so flashtimer can read your active Price Rules and surface real sale end-dates directly in the timer editor. Instead of typing a deadline manually, you pick a discount rule and the countdown is set automatically.

Since January 1, 2026 Shopify no longer lets stores create new custom apps under Develop apps. The steps below work for a custom app your store already has; if yours doesn't, contact info@byte2bit.com and we'll help you connect.

Setup

  1. In Shopify Admin go to Settings → Apps and sales channels → Develop apps and click Create an app.
  2. Name it (e.g. flashtimer), open its Configuration tab, then click Configure under Admin API integration.
  3. Enable the following scopes under Admin API integration:
    • read_discounts — required to list your discounts in the timer editor and keep their timers in sync: flashtimer reads each discount when Shopify reports a change, and registers its webhook listeners automatically (Shopify doesn't require a separate scope for that). Without it, no discount syncs.
    • read_price_rules — also required to list your discounts: flashtimer reads the ones made through Shopify's older Price Rules API too, so their timers still match.
    • read_products — required for the Shopify Stock Monitor feature (list products and stock levels in the timer editor).
    • read_inventory — required for the Stock Monitor to read per-location inventory quantities.
    • read_rollouts — also required to list and sync your discounts: flashtimer checks whether a Rollout decides who gets each one, and leaves that discount's timer as it is while one does. A token without it isn't accepted.
    Save.
  4. Click Install app to activate it.
  5. Copy the Admin API access token (starts with shpat_) — Shopify shows it only once.
  6. Also copy your store's .myshopify.com domain from the admin URL.
  7. Paste both under Settings → Integrations → Shopify.

What it unlocks

  • Active Price Rules with an ends_at date appear in the timer editor under Pick a sale end date.
  • Select a rule and the countdown deadline fills in automatically — no copy-paste needed.
  • Discounts with ends_at: null (always-on) are shown as context but don't set a deadline.
  • flashtimer fetches up to 50 discounts per request, ordered by most recent.

Example discounts response

// Example discounts returned from Shopify
[
  { "id": "123456789", "title": "Black Friday 20% Off", "endsAt": "2026-11-28T00:00:00Z" },
  { "id": "987654321", "title": "Cyber Monday",         "endsAt": "2026-12-01T00:00:00Z" },
  { "id": "111222333", "title": "Always-on member deal","endsAt": null }
]

Automatic sync via webhooks

When you save your Shopify credentials, flashtimer automatically registers webhook listeners in your store for discounts/create, discounts/update, and discounts/delete. The legacy Price Rules API (price_rules/create, price_rules/update, price_rules/delete) is also supported for stores that haven't migrated to the modern Discounts API.

Enabling automation

Webhook sync is off by default. Under Settings → Integrations → Shopify you'll find three toggles under Automation:

  • Auto-create timers — a new timer is created when a discount is added in Shopify that has an end date.
  • Auto-update timers — the linked timer is updated whenever the discount's title or end date changes.
  • Auto-archive timers — the timer is archived when the discount is deleted or deactivated in Shopify.

Webhook verification

Every incoming webhook is verified with HMAC-SHA256 using your Shopify Custom App's API Secret Key. Add it to the Webhook Signing Secret field in the Shopify integration settings — without it, flashtimer will reject all incoming webhook calls. The API Secret Key is on the API credentials tab of your Custom App in Shopify Admin.

Webhooks are received at:

POST https://api.flashtimer.com/webhooks/shopify

Shopify Stock Monitor

The Stock Monitor lets you tie any timer to a Shopify product's inventory. When the product's total stock drops to or below a threshold you define, flashtimer automatically expires the timer — every email opened after the sell-out shows the expiry message instead of a live countdown.

Additional scopes required

Your Shopify Custom App must have two extra scopes beyond the base integration:

  • read_products — allows flashtimer to list your products and their stock levels.
  • read_inventory — allows flashtimer to read per-location inventory quantities.

Adding scopes to an existing app

  1. In Shopify Admin go to Settings → Apps and sales channels → Develop apps and open your flashtimer app.
  2. Click Configuration → Admin API integration → Edit.
  3. Enable read_products and read_inventory, then click Save.
  4. Go to the API credentials tab and click Reinstall app — Shopify generates a new access token with the updated scopes. Copy it (shown only once).
  5. In flashtimer go to Settings → Integrations → Shopify, click Disconnect Shopify, then reconnect with your new token.

How to use it

  1. Open any timer in the editor and click the ⋯ actions menu in the top-right corner.
  2. Select Shopify stock. A dialog lists all your Shopify products with their current stock levels.
  3. Click the product you want to monitor, set the Expire when stock ≤ threshold (e.g. 0 to expire only when completely sold out, or 10 for a low-stock trigger), and click Save.
  4. flashtimer checks every 5 minutes. As soon as the condition is met, the timer is marked as expired — every email opened from then on shows the expiry message straight away.
Stock badge colours in the dialog: Products currently at or below your threshold appear with a red badge so you can see which items are already low before saving.

Abandoned Cart Recovery

The first time your store fires an abandoned checkout, flashtimer automatically creates one starter evergreen timer named "Abandoned Cart Recovery" (a 30-minute countdown that starts fresh for each customer the moment they open the email) so you have something ready to drop into your own cart-recovery email flow right away. This only ever happens once per store — if you delete the timer it will not be re-created.

flashtimer never reads or stores anything from the checkout itself — no customer email, name, cart contents, or address. The webhook is used purely as a signal that a checkout was abandoned; the per-recipient countdown runs entirely through the same evergreen merge-tag mechanism used by every other evergreen timer.

Requires Shopify's Protected Customer Data approval. checkouts/create is one of Shopify's protected-data webhook topics — Shopify will not deliver it on a live store until this is granted, regardless of scopes. For a Custom App connection, request it yourself under your app's API access settings in Shopify Admin; it does not require anything from flashtimer. Until it's granted, this feature is silently inactive rather than broken — everything else in the Shopify integration is unaffected.

Additional scopes required

  • read_checkouts and read_orders — required by Shopify to subscribe to checkout webhook topics. Add them the same way described above for Stock Monitor, then reconnect.

Make

Connect Make (formerly Integromat) to send timer data to any of Make's 2,000+ supported apps and services — CRMs, project tools, databases, and more — using a visual no-code workflow. No code required.

Setup

  1. In Make create a new Scenario and add a Webhooks → Custom Webhook module as the trigger.
  2. Click Add to create the webhook, give it a name, and copy the generated URL.
  3. Paste the URL under Settings → Integrations → Make.
  4. Add action modules to your Scenario (e.g. Google Sheets, Slack, Airtable) and activate it.

What it unlocks

  • flashtimer POSTs a JSON payload to your Make webhook when you click Send timer data.
  • Chain as many Make modules as you need — update a CRM, log to a sheet, send a Slack alert, all in one scenario.
  • The Make panel in the timer editor shows whether the last send succeeded or failed.

Webhook payload

{
  "timerName": "Black Friday Sale",
  "timerUrl": "https://timers.flashtimer.com/xK3mZ9pQvT.gif",
  "gifUrl": "https://timers.flashtimer.com/xK3mZ9pQvT.gif",
  "expiresAt": "2026-11-27T23:59:00-05:00[America/New_York]"
}

expiresAt is null for an evergreen, recurring, cycle, count-up or dynamic timer, which has no single end date. (Evergreen timers used to send the placeholder 2099-01-01T00:00:00[UTC] here.)

WooCommerce

Connect your WooCommerce store to surface active sale end dates from your coupons directly in the timer editor. Instead of entering a deadline manually, pick a coupon and flashtimer fills it in automatically.

Setup

  1. In your WordPress admin go to WooCommerce → Settings → Advanced → REST API.
  2. Click Add key, set Permissions to Read/Write (writing is what lets flashtimer register the coupon webhooks that keep timers in sync), and click Generate API key.
  3. Copy the Consumer key and Consumer secret — they are shown only once.
  4. Enter your store URL (e.g. https://yourstore.com), Consumer key, and Consumer secret under Settings → Integrations → WooCommerce.

What it unlocks

  • Active coupons with a date_expires are listed in the timer editor under Pick a sale end date.
  • On-sale products (fetched via on_sale=true) are also surfaced alongside coupons, so you can link a timer to a product sale end date too.
  • Select a coupon or product and the countdown deadline is set automatically.
  • Items with no expiry date are shown for reference but won't set a deadline.

Example coupon response

[
  { "id": 101, "name": "Black Friday 20% Off", "date_expires": "2026-11-28T00:00:00Z" },
  { "id": 102, "name": "Cyber Monday Flash",   "date_expires": "2026-12-01T00:00:00Z" }
]

Automatic sync via webhooks

When you save your WooCommerce credentials, flashtimer automatically registers webhook listeners in your store for coupon.created, coupon.updated, coupon.deleted, and coupon.restored via the WooCommerce REST API. Each webhook is signed with a per-org HMAC-SHA256 secret that flashtimer generates automatically — no manual configuration needed.

Enabling automation

Webhook sync is off by default. Under Settings → Integrations → WooCommerce you'll find three toggles under Automation:

  • Auto-create timers — a new timer is created when a coupon with a date_expires_gmt is added.
  • Auto-update timers — the linked timer is updated when the coupon's code or expiry changes.
  • Auto-archive timers — the timer is archived when the coupon is deleted, and restored when it is un-deleted.

Webhooks are received at:

POST https://api.flashtimer.com/webhooks/woocommerce/{orgId}

The orgId is your organization's unique identifier — flashtimer uses it to look up your credentials and route the webhook to the correct account. It's included automatically in the webhook URL registered in your WooCommerce store.

BigCommerce

Connect your BigCommerce store to see active promotion and coupon end dates in the timer editor. flashtimer reads your store's promotions via the V2/V3 API so you can align countdowns to your discount expiry without switching tabs.

Setup

  1. In your BigCommerce control panel go to Settings → API → API Accounts and click Create API Account → Create V2/V3 API Token.
  2. Give it a name and set the Promotions scope to Read-only. All other scopes can remain None.
  3. Click Save and copy the Access Token.
  4. Find your Store Hash — it's in the API Path shown on the same page: https://api.bigcommerce.com/stores/{storeHash}/v3/.
  5. Enter both under Settings → Integrations → BigCommerce.

What it unlocks

  • Active promotions with an expires date are listed in the timer editor.
  • Select a promotion and the countdown deadline is set automatically.
  • Promotions without an expiry are shown for context but won't set a deadline.

Example promotions response

[
  { "id": 501, "name": "Black Friday Promo",  "expires": "2026-11-28T00:00:00+00:00" },
  { "id": 502, "name": "Holiday Sale",        "expires": "2026-12-26T00:00:00+00:00" }
]

Other email platforms

These platforms work the same way as Klaviyo or Mailchimp above: connect them under Settings → Integrations with the credentials the dialog asks for, then open a timer's Send to ESP panel, pick a campaign (a draft, or a scheduled one where the platform lets it be changed) or a template and click Embed Timer.

HubSpot, Campaign Monitor, ConvertKit, Iterable, Beehiiv, SendGrid, Mailgun, Postmark, Mailjet, MoEngage, Marketo, Amazon SES.

For these, copy the timer's ready-made HTML snippet from Embed in the timer editor and paste it into your campaign: Pardot, AWeber, Braze, Customer.io, Bloomreach, CleverTap, Keap, Emma, Zoho Campaigns, Retention.com.

Any other email platform works too: copy the timer's image URL or HTML from Embed in the timer editor and paste it into an image or HTML block.