Start, pause, resume, and reset a timer

Duration timers are the only type you can control after creation, and POST /timers/{timer_id}/actions is how. Four actions, one field, and the change appears on the hosted page and every embed immediately. A fixed-date countdown cannot be paused — its deadline is a moment on the calendar, so you move it by updating deadline_at instead.

The call

One field, one of four values. No Idempotency-Key and no If-Match — actions do not create anything and do not carry the lost-update risk that updates do.

Pause a running timer

curl -X POST \
  https://countdownshare.com/api/v1/timers/$TIMER_ID/actions \
  -H "Authorization: Bearer $COUNTDOWNSHARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "action": "pause" }'
actionValid fromEffect on remaining time
startscheduledBegins counting down from the full duration_seconds.
pauserunningFreezes remaining time at its current value. Nothing is lost.
resumepausedContinues from exactly where it froze.
resetany stateReturns to the full duration and stops. Send start again to run it.
Sending an action a timer cannot accept — resume on something that is running, or any action on a fixed-date countdown — returns HTTP 400 with validation_error. Read the current state from the status endpoint first if you are not certain what it is.

The state machine

A new duration timer is scheduled — created but not counting. It stays there until something starts it, which is the behaviour you want when the clock should begin at a moment you choose rather than at the moment the record was created.

Duration timer lifecycle

  create            start           pause          resume
     |                |               |               |
     v                v               v               v
 scheduled -------> running -------> paused -------> running
                       |                                |
                       |             reset              |
                       +---------------+----------------+
                                       |
                                       v
                                   scheduled
                          (back to the full duration)

 running --- duration elapses ---> ended

reset is the only action valid from any state, including ended. It is how you reuse a timer rather than creating a new one — worth knowing, because reuse is free and creation counts against your monthly allowance.

What pausing actually does

It freezes the remaining time, not the deadline. The distinction matters: a timer paused with 4 minutes left and resumed an hour later still has 4 minutes left, not minus 56.

Pause and resumeReset
Remaining time afterExactly what it wasThe full duration_seconds
Resulting staterunningscheduled — it does not auto-start
Valid fromrunning / pausedAny state, including ended
Webhook eventtimer.paused / timer.resumedtimer.reset
This is the property that makes duration timers right for anything where your own latency should not eat the customer's window — payment authorisation, a manual review step, an upstream service being slow. Freeze the clock, do the work, hand the time back untouched.

What viewers see

Actions propagate to every output, but not all of them behave identically because an email client is not a browser.

Hosted page

Reflects the change immediately. A paused countdown stops on screen for anyone watching.

Website embed

Same as the hosted page — it is the same renderer inside an iframe.

Email GIF

Uses the timer state at the moment the inbox requests it. A running timer returns a 30-frame animated GIF; a paused or reset one returns a single-frame GIF.

The email behaviour follows from how email works: the GIF is generated when it is fetched, and it cannot update afterwards. Pausing a timer does not change a GIF already rendered in an inbox — it changes what the next fetch produces. More on this under embeds and hosted pages.

Every action emits an event

Which means you can drive your own logic from a control action without polling. Attach a rule with condition.field: "action" and the value you care about.

ActionEvent typeA reason to listen
starttimer.startedMark the stock as reserved the moment the hold begins.
pausetimer.pausedAlert an operator that a session has stalled mid-checkout.
resumetimer.resumedResume a matching process on your side.
resettimer.resetLog that the window was extended, and by whom.
— (reaches zero)timer.completedRelease the reservation. This is the one that matters most.
timer.completed is not an action — it fires on its own when the countdown reaches zero. Listening for it is almost always better than polling the status endpoint waiting for ended, because it arrives at the moment it happens rather than at your next poll. See webhook rules.

A worked example

A cart reservation is the canonical case: the clock should start when the customer commits, stop while you are waiting on a third party, and reset cleanly on a retry.

Checkout hold with pause during authorisation

// A checkout hold: 15 minutes, paused while payment is
// authorising so the customer is not charged for our latency.

// 1. Reserve the stock and start the clock.
await action(timerId, "start");

// 2. The payment provider is taking its time — freeze the clock.
await action(timerId, "pause");

const result = await chargeCard(order);

if (result.requiresAction) {
  // 3a. 3-D Secure. Hand the remaining time back, unchanged.
  await action(timerId, "resume");
} else if (result.failed) {
  // 3b. Give the full window back for a retry with another card.
  await action(timerId, "reset");
  await action(timerId, "start");
}

async function action(id, name) {
  return call(`/timers/${id}/actions`, {
    method: "POST",
    body: JSON.stringify({ action: name }),
  });
}

Other places this shape fits

  • Online exams — pause for a technical issue, resume with the candidate's time intact.
  • Support SLAs — the clock stops while a ticket is waiting on the customer.
  • Live events and workouts — interval timers a presenter or instructor controls from their own tooling.
  • Booking holds — the seat is yours for ten minutes, and reset gives a fresh window without a new timer.
All of these reuse one timer rather than creating one per attempt. Actions are free and unlimited; creation is the metered operation. Building around reset instead of delete-and-recreate is meaningfully cheaper at volume.