Jenkins build periodically: cron syntax and the H symbol
Jenkins' Build periodically takes 5 cron fields plus H, a hash of the job name that spreads start times: H 20 * * * runs once in the 8 p.m. hour.

Jenkins' Build periodically trigger, and triggers { cron('…') } in a Declarative Pipeline, take cron's five fields, MINUTE HOUR DOM MONTH DOW, plus the symbol H: a hash of the job name that picks a stable value inside a range, so scheduled jobs don't all start at once. H 20 * * * builds once a day at a fixed minute in the 8 p.m. hour; 0 20 * * * builds at exactly 8:00 p.m.
Jenkins facts come from its Pipeline Syntax and Using a Jenkinsfile docs and from the Build periodically help text in Jenkins' source; Sume facts come from Create a run, Runs and results and Authentication. All were read on 2026-09-28. Sume has no Jenkins plugin: the build makes a plain HTTPS call with curl.
What is the Jenkins cron syntax?
Five fields separated by spaces or tabs: minute (0–59), hour (0–23), day of month (1–31), month (1–12) and day of week (0–7, where 0 and 7 are Sunday). Each field takes *, ranges, steps and lists. The Build periodically field takes one schedule per line, and lines starting with # are comments. Other schedulers count fields differently; Cron expression with 6 fields compares them.
| Goal | Schedule | What Jenkins does |
|---|---|---|
| Every day in the 8 p.m. hour | H 20 * * * | One build at a minute hashed from the job name |
| Every day at exactly 8:00 p.m. | 0 20 * * * | Every job with this line starts in the same minute |
| Every fifteen minutes | H/15 * * * * | Perhaps at :07, :22, :37 and :52 |
| Once a day between midnight and 7:59 a.m. | H H(0-7) * * * | H limited to a range |
| Every two hours, 9:45 a.m. to 3:45 p.m., weekdays | 45 9-16/2 * * 1-5 | Fixed minutes, no hash |
| Once an hour | @hourly | The same as H * * * * |
| Once a night | @midnight | Some time between 12:00 a.m. and 2:59 a.m. |
What does H mean in a Jenkins cron expression?
H stands for hash. Jenkins asks you to use it wherever possible so periodic jobs produce an even load: 0 0 * * * on a dozen daily jobs causes a large spike at midnight, while H H * * * still runs each job once a day, just not all at the same time. It isn't random: the value is a hash of the job name, so it stays stable for a given project.
H also takes ranges and steps. In the day-of-month field, hashes are chosen in the 1–28 range, so H/3 can leave a gap of 3 to 6 days at the end of a month. The @yearly, @monthly, @weekly, @daily, @midnight and @hourly aliases use the same hashing.
Which time zone does Build periodically use?
By default, the time zone of the Jenkins controller's JVM. To change it, start the schedule with a TZ= line and a time zone ID, such as TZ=Europe/London above H 8 * * *. The IDs you can use depend on the Java runtime Jenkins runs on.
How do I call a paid API from a scheduled build?
Sume's docs list CI secret stores among the places a key may live, so store it as a Secret text credential and bind it in the environment block with credentials('sume-api-key'). Jenkins prints **** if the value is echoed, which it says only reduces the risk of accidental exposure. Keep the sh script in single quotes so the shell expands $SUME_API_KEY: Jenkins warns that Groovy interpolation copies the secret into process arguments that tools such as ps can show. Key the request to the build's date, and keep the body the same all day.
pipeline {
agent any
triggers { cron('H 20 * * *') }
environment { SUME_API_KEY = credentials('sume-api-key') }
stages {
stage('Start the nightly video') {
steps {
sh '''
curl -sS --fail-with-body --max-time 30 --retry 3 \
https://api.sume.com/v1/formats/acme/nightly-recap/runs \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: nightly-recap-$(date -u +%F)" \
-d '{"input":{"feed_url":"https://example.com/nightly.json"},"communication":{"webhook_url":"https://example.com/hooks/sume"}}'
'''
}
}
}
}What happens if the build retries or runs twice?
Nothing new is charged. options { retry(3) } re-runs a failed Pipeline, and a manual build later the same UTC day sends the same date key. Sume answers the same key and body with 200 and the original run, idempotency_hit: true: no second run, no second charge. Two builds at the same moment get one 409 idempotency_key_in_use, retryable after about a second. A deliberate second video on the same date needs a new key, such as one ending in -v2.
Should the build wait for the video?
It doesn't need to. The create answers immediately with a receipt and the run itself takes minutes; the communication.webhook_url in the body gets one signed POST when the run completes or fails. If the build must poll instead, double the gap between reads up to a minute and set options { timeout(time: 2, unit: 'HOURS') } above the run's deadline, which is at most 90 minutes after it was created. A Jenkins timeout aborts the build, not the run: abandoning a poll loop does not stop the run or its spend. GitLab scheduled pipeline: run a nightly AI video job shows such a loop.
Sources
- Create a run
- Runs and results
- Errors and spend
- Authentication
- Jenkins: Pipeline Syntax (read 2026-09-28)
- Jenkins: Using a Jenkinsfile (read 2026-09-28)
- Jenkins source: Build periodically schedule help (help-spec.jelly) (read 2026-09-28)
- Jenkins source: trigger messages (Messages.properties) (read 2026-09-28)
Related posts
More in Integrations
- Kilo Code MCP server: add Sume in kilo.jsonc
Add Sume's hosted MCP server to Kilo Code under the mcp key in kilo.jsonc: type remote, the Sume URL, and an API key header or an OAuth sign-in.
- Kling API in n8n: HTTP Request node, auth, and waiting
Call the Kling API from n8n with an HTTP Request node and a Bearer credential, then poll or resume a Wait node. Direct to Kling, or kling-3 through Sume.
- Langflow MCP client: add Sume's hosted MCP server
Use Langflow as an MCP client for Sume's hosted server: register it with a Bearer global variable, then wire MCP Tools into an Agent.
- LibreChat MCP server: add Sume in librechat.yaml
Add Sume's hosted MCP server to LibreChat under mcpServers in librechat.yaml: streamable-http, a Bearer key header, and requiresOAuth set to false.
Written by Sume