---
title: "Delayed messages: reminders with timers"
description: "Ask for a review a day after checkout: a timer keyed by the booking is scheduled, moved or cancelled, and fires as a real message in the booking's partition."
---

> Queen MQ documentation, for AI agents
> Complete self-contained summary of Queen MQ: https://queenmq.com/llms-brief.txt
> Fetch that first when the question is about the product rather than about this page.
> Index of all pages: https://queenmq.com/llms.txt

# Delayed messages: reminders with timers

A day after an order is delivered, ask for a review. If the delivery is late, move the reminder. If the guest already wrote a review, cancel it. The usual answer is a cron job that scans a table every minute.

A timer is a message that waits. It is named by its queue and a key: scheduling the same key again moves it, and cancelling the key removes it. When it is due, it arrives in its queue as a normal message, through the log.

**Figure.** A timeline for the reminder review-2001 in the queue review-reminders. Scheduled at delivery for 24 hours later: status scheduled. The delivery is late, so the same key is scheduled again for 48 hours: status rescheduled, and the first time is gone. The guest writes a review, so the key is cancelled. A timer that is not cancelled fires as a normal message of its queue, through the log.

A keyed timer: schedule, move, cancel.

JavaScript:

```js
// When an order is delivered, ask for a review in 24 hours.
await queen.timer('review-reminders').key('review-2001').delay('24h')
  .payload({ orderId: 2001 }).schedule()          // status: 'scheduled'

// The delivery was late: schedule the same key again, and the reminder moves.
await queen.timer('review-reminders').key('review-2001').delay('48h')
  .payload({ orderId: 2001 }).schedule()          // status: 'rescheduled'

// The guest wrote a review already: no reminder at all.
await queen.timer('review-reminders').key('review-2001').cancel()
```

Python:

```python
# When an order is delivered, ask for a review in 24 hours.
await (
    queen.timer("review-reminders").key("review-2001").after(timedelta(hours=24))
    .payload({"orderId": 2001}).schedule()            # status: 'scheduled'
)

# The delivery was late: schedule the same key again, and the reminder moves.
await (
    queen.timer("review-reminders").key("review-2001").after(timedelta(hours=48))
    .payload({"orderId": 2001}).schedule()            # status: 'rescheduled'
)

# The guest wrote a review already: no reminder at all.
await queen.timer("review-reminders").key("review-2001").cancel()
```

Go:

```go
// When an order is delivered, ask for a review in 24 hours.
r, err := client.Timers().Schedule(ctx, queen.TimerSchedule{
	Queue: "review-reminders", TimerKey: "review-2001", Delay: 24 * time.Hour,
	Payload: map[string]interface{}{"orderId": 2001},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(r.Status) // scheduled

// The delivery was late: schedule the same key again, and the reminder moves.
r, err = client.Timers().Schedule(ctx, queen.TimerSchedule{
	Queue: "review-reminders", TimerKey: "review-2001", Delay: 48 * time.Hour,
	Payload: map[string]interface{}{"orderId": 2001},
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(r.Status) // rescheduled

// The guest wrote a review already: no reminder at all.
_, err = client.Timers().Cancel(ctx, "review-reminders", "review-2001")
if err != nil {
	log.Fatal(err)
}
```

Rust:

```rust
// When an order is delivered, ask for a review in 24 hours.
let scheduled = queen
    .timers()
    .schedule("review-reminders", "review-2001", Duration::from_secs(24 * 3600))
    .payload_json(&json!({ "orderId": 2001 }))?
    .send()
    .await?; // status: Scheduled

// The delivery was late: schedule the same key again, and the reminder moves.
let moved = queen
    .timers()
    .schedule("review-reminders", "review-2001", Duration::from_secs(48 * 3600))
    .payload_json(&json!({ "orderId": 2001 }))?
    .send()
    .await?; // status: Rescheduled

// The guest wrote a review already: no reminder at all.
queen.timers().cancel("review-reminders", "review-2001").await?;
```

PHP:

```php
// When an order is delivered, ask for a review in 24 hours.
$timer = $queen->timers()->schedule('review-reminders', 'review-2001', 24 * 3600 * 1000,
    ['orderId' => 2001]);                           // $timer['status']: 'scheduled'

// The delivery was late: schedule the same key again, and the reminder moves.
$timer = $queen->timers()->schedule('review-reminders', 'review-2001', 48 * 3600 * 1000,
    ['orderId' => 2001]);                           // $timer['status']: 'rescheduled'

// The guest wrote a review already: no reminder at all.
$queen->timers()->cancel('review-reminders', 'review-2001');
```

C++:

```cpp
// When an order is delivered, ask for a review in 24 hours.
queen::TimerSchedule reminder;
reminder.queue = "review-reminders";
reminder.timer_key = "review-2001";
reminder.payload = {{"orderId", 2001}};
reminder.delay_millis = 24 * 60 * 60 * 1000;
json first = client.timers().schedule(reminder);   // status: "scheduled"

// The delivery was late: schedule the same key again, and the reminder moves.
reminder.delay_millis = 48 * 60 * 60 * 1000;
json moved = client.timers().schedule(reminder);   // status: "rescheduled"

// The guest wrote a review already: no reminder at all.
client.timers().cancel("review-reminders", "review-2001");
```

The first call answered `scheduled` and the second `rescheduled`; after the cancel, no reminder was left. A short timer of the same queue arrived as a message, with its payload as data.

**Why:** the key makes a reminder easy to move or cancel, and a retried schedule is harmless. Put the timer in the [transaction](/writing-applications/transactions/) of the step that creates it, and it exists exactly when the step committed.

One thing to know: a cancel can arrive just after the timer fired. The consumer of the reminder should check the order's state before it sends anything.

Built from: [KV, locks and timers](/writing-applications/kv-locks-timers/#timers).

Source: https://queenmq.com/patterns/delayed-messages/index.mdx
