Scheduled webhooks

Scheduled webhooks

A scheduled webhook fires on a CRON timer instead of on a Jira event. JQL on the schedule is the source of the issue list; the whole list ships in one delivery. Times are always UTC; real fire is 0–5 minutes after the planned minute.


When to use it

Reach for a scheduled webhook when:

  • You need a periodic snapshot of Jira state — daily / hourly / weekly.

  • Your receiver expects batched input rather than one event at a time.

  • You want a heartbeat from Webhooks Pro at a known cadence.

  • The trigger is time — not a Jira event.

If you're trying to react to something happening in Jira, a scheduled webhook is the wrong tool. Use an event-driven webhook (see Webhooks).


Scheduled vs. event-driven — at a glance

 

Event-driven

Scheduled

 

Event-driven

Scheduled

Trigger

Jira event matching the subscription

A wall-clock minute matching the CRON

Frequency

Whenever the event happens

Fixed cadence you define

JQL

Optional — narrows which events qualify

Required — defines the issue list to send

Payload shape

issue: { … } (one issue per delivery)

issues: [ … ] (whole list per delivery)

event in body

avi:jira:created:issue etc.

'schedule'

Timezone

n/a

Always UTC

Timing precision

Within the debounce window

0–5 minutes after the planned minute

Schedules are designed for periodic reports, reminders, daily digests — not for time-critical workflows.


How it works

Scheduled webhooks - How it works.png

 

Forge runs the underlying scheduled trigger every 5 minutes on a fixed (per-tenant) cadence. On each tick the scheduler asks: which CRON expressions matched a minute in the last 5-minute window? For each match, it fetches matching issues by JQL (paged, 50 per page server-side), renders the payload, and sends one HTTP request.


Configure

The Schedule card on the webhook form has one toggle — Enable scheduled delivery — and two input modes:

  • Builder (default): tabs for Minutes / Hours / Day / Month with Every, Every N, Specific values. The Day tab combines Day-of-Month and Day-of-Week; selecting one clears the other (Quartz forbids both being specific).

  • Custom cron: paste a Quartz 7-field expression directly.

A live preview under the inputs shows the composed expression, a human-readable label (e.g. "At 12:00, Mon–Fri"), and a UTC marker.

Required: JQL on the Events tab

A scheduled webhook must have a JQL filter set on the Events tab — the JQL is the source of the issue list. Without JQL, there's nothing to send.

If you want a recurring webhook that doesn't carry issue data (e.g. a heartbeat), use a JQL that you know returns nothing (key = INVALID-0 works) plus the Send request even if JQL returns no issues toggle.

Optional: Send empty deliveries

By default, if JQL returns 0 issues at fire time, the delivery is skipped — no HTTP request, no log row. Enable Send request even if JQL returns no issues to deliver an empty issues: [] anyway.


Sub-pages


Examples

CRON

Means

CRON

Means

0 */5 * * * ? *

Every 5 minutes

0 0 * * * ? *

Every hour at :00

0 0 9 ? * MON-FRI *

At 09:00 UTC, Monday–Friday

0 30 14 1 * ? *

14:30 UTC on the 1st of every month

0 0 0 1 1 ? *

New Year — 00:00 UTC on Jan 1

Full reference and a UTC ↔ local cheat sheet: Schedule examples.


Limits and gotchas

  • Always UTC. The form does not convert from your timezone. 0 0 9 ? * MON-FRI * fires at 09:00 UTC, which is 11:00 in Berlin (summer). See UTC semantics and the fire-window.

  • 5-minute grid for minutes. The minute field must be */5 or specific values from 0,5,10,…,55. Anything else (e.g. minute 3) is silently dropped — the next tick evaluates only the 5-minute boundaries. The form's validator catches most of these on save; pasted custom-cron can slip through.

  • 0–5 minute lag. A scheduled webhook will never fire earlier than the CRON time, but can be up to 5 minutes late depending on where the per-tenant Forge tick lands.

  • Whole list per delivery, not per issue. A schedule that matches 200 issues sends one HTTP request with all 200 in issues: []. If the receiver requires one-issue-per-call, post-process on the receiver side or use an event-driven webhook with similar JQL.

  • No catch-up. If a tick is missed (Forge maintenance, disabled app), the missed fire is not replayed. The next tick evaluates only the next CRON match.

  • Schedules don't fire while the app is uninstalled. Reinstalling resets the per-tenant tick offset.

  • Auto-disable applies. A scheduled webhook can be auto-disabled after N consecutive failures, just like an event-driven one.


Related

  • Webhooks — the parent. Most form sections are shared.

  • JQL filtering — the JQL on a schedule's Events tab. Semantics differ slightly from event-driven (it's the source list, not a filter).

  • Payload templates — schedules render the same templates, just with issues: [ … ] instead of issue: { … }.

  • Webhook logs — every fired schedule produces one log row (or none, if empty + heartbeat disabled).

  • CRON didn't fire when expected (UTC pitfalls) — diagnostic playbook.