watches
Watches

Database Watches

Watches are event-driven triggers. Instead of running on a fixed schedule, DataRunner polls your database at a configurable interval and fires the moment new rows match your SQL condition — no scheduled times, no delays.

Read
7 min
Sections
7
Min interval
60 s

How watches work

A watch has three parts:

  1. Trigger querya SQL SELECT that returns rows when your condition is met
  2. Poll intervalhow often DataRunner runs the trigger query (minimum 60 seconds)
  3. Actionwhat DataRunner does when new rows are found

On every poll, DataRunner runs the trigger query and compares the results to the previous poll. If new rows appeared since the last poll, the watch fires its configured action. Rows that already triggered the watch will not trigger it again — DataRunner tracks which rows have fired using a unique identifier column you specify.

Creating a watch

Go to Watches → New watch. Configure:

  • Namea descriptive label for this watch
  • Connectionwhich database to poll
  • Trigger querySQL that returns the rows you want to watch for
  • Unique ID columna column (usually a primary key or UUID) that uniquely identifies each row, used for deduplication
  • Poll intervalhow often to check, in seconds (minimum 60)
  • Actionwhat to do when new rows are found

Trigger conditions

The trigger query can be any SELECT statement. DataRunner fires whenever this query returns rows that weren't returned in the previous poll. There are no restrictions on query complexity — you can use joins, CTEs, subqueries, and window functions.

New orders in the last 5 minutes
Example
SELECT id, customer_id, total, created_at
FROM orders
WHERE created_at > NOW() - INTERVAL '5 minutes'
Failed payment attempts
Example
SELECT id, user_id, amount, error_code, attempted_at
FROM payment_attempts
WHERE status = 'failed'
  AND attempted_at > NOW() - INTERVAL '1 hour'
Inventory below threshold
Example
SELECT sku, product_name, stock_qty
FROM inventory
WHERE stock_qty < 10
  AND active = true
Background job failures
Example
SELECT id, job_type, error_message, failed_at
FROM background_jobs
WHERE status = 'failed'
  AND failed_at > NOW() - INTERVAL '2 minutes'
Best practice: Always filter by a recent time window (e.g. created_at > NOW() - INTERVAL '5 minutes') to keep queries fast and avoid scanning the full table on every poll.

Watch actions

When a watch fires, it can perform one or more of the following actions:

Email alert

Send an email to configured recipients. The email body can include the number of new rows matched and a summary of the trigger data. Configure in Settings → Email first.

Slack notification

Post a message to a Slack channel or DM. Include a row count summary and trigger details in the message body. Requires a Slack integration in Settings → Channels.

Webhook

POST a JSON payload to an HTTP endpoint. The payload includes the watch name, trigger timestamp, matched row count, and the full result rows as a JSON array. Useful for integration with PagerDuty, OpsGenie, Zapier, or custom backends.

webhook payloadjson
{
  "watch_id": "wt_01hxyz",
  "watch_name": "Failed payments",
  "triggered_at": "2026-05-28T14:23:01Z",
  "matched_rows": 3,
  "rows": [
    { "id": 4821, "user_id": 99, "amount": 49.00, "error_code": "card_declined" },
    ...
  ]
}

Full report delivery

Instead of just an alert, deliver the complete result set as a formatted report (CSV, Excel, or PDF) to any configured channel. This is the same delivery mechanism as scheduled reports — the difference is the trigger is event-driven rather than time-based.

Poll interval and performance

The minimum poll interval is 60 seconds. For most use cases, 5—15 minutes is a good starting point. Shorter intervals mean more frequent queries — balance responsiveness with database load.

Database load: Each poll runs your trigger query against the production database. Make sure your trigger query is indexed appropriately. Use EXPLAIN ANALYZE in the workbench to verify query performance before setting a short poll interval.

Deduplication

DataRunner uses the Unique ID column you configure to track which rows have already fired. A row that matched the trigger on a previous poll will not trigger the action again — even if it continues to appear in the trigger query results.

This means your trigger query does not need to track "already-seen" rows itself — DataRunner handles deduplication. You can write a simple recency-based query and DataRunner ensures each row triggers the action exactly once.

Watch history

The History tab on each watch shows every trigger event: timestamp, matched row count, and delivery confirmation per action. You can drill into any event to see the full result set that matched.