Azure DevOps scheduled pipeline: a nightly cron in UTC
Add a schedules block with a UTC cron to the pipeline YAML, set always: true to run without code changes, and key any paid API call to the date.

To schedule an Azure DevOps pipeline, add a schedules: block to its YAML with a cron: expression, which Azure Pipelines reads in UTC, and the branches: it applies to. By default a scheduled run is skipped when nothing changed since the last successful scheduled run, so add always: true to a pipeline that must run every night, such as one that starts an AI video.
Azure facts come from Microsoft's Configure schedules to run pipelines and the other Azure Pipelines pages under Sources; Sume facts come from Create a run, Runs and results and Errors and spend. All were read on 2026-09-28. Sume has no Azure DevOps integration: the pipeline makes one plain HTTPS call with curl. The same job on GitLab is GitLab scheduled pipeline: run a nightly AI video job.
What does a scheduled pipeline look like in YAML?
Each entry under schedules: takes a five-field cron: in single quotes, a displayName, branches with include and exclude lists, and two booleans that default to false: always runs even when nothing changed, and batch stops the schedule from starting a run while an earlier run is still in progress, except when always is true. This pipeline starts one Sume Format run at 05:00 UTC every day:
trigger: noneandpr: noneturn off the default CI and PR triggers.namemakes the run number start with the date, which Azure DevOps Services expresses in UTC. The step builds itsIdempotency-Keyfrom that date.-H @-makes curl read the header from standard input, and--fail-with-bodymakes it return an error on a4xxor5xxwhile still printing Sume's JSON error.
name: $(Date:yyyyMMdd).$(Rev:r) # the run number starts with the date
trigger: none
pr: none
schedules:
- cron: '0 5 * * *' # 05:00 UTC every day
displayName: Nightly video
branches:
include: [main]
always: true # run even when nothing changed
jobs:
- job: nightly_video
timeoutInMinutes: 10
steps:
- bash: |
DAY="${RUN_NUMBER%%.*}" # same date on every retry of this run
printf 'Authorization: Bearer %s\n' "$SUME_API_KEY" |
curl -sS --fail-with-body -H @- -H 'Content-Type: application/json' \
-H "Idempotency-Key: nightly-recap-$DAY" \
-d "{\"input\":{\"day\":\"$DAY\"},\"communication\":{\"webhook_url\":\"https://example.com/hooks/sume\"}}" \
https://api.sume.com/v1/formats/acme/nightly-recap/runs
retryCountOnTaskFailure: 2
env:
SUME_API_KEY: $(SUME_API_KEY) # a secret variable
RUN_NUMBER: $(Build.BuildNumber)Why didn't my scheduled pipeline run?
Start with Scheduled runs in the pipeline's context menu: it previews upcoming scheduled runs, up to seven days ahead. If the run you expect is missing, or listed but never started, Microsoft's page names these causes:
| Symptom | Cause | Fix |
|---|---|---|
| No run on quiet days | By default the pipeline doesn't run as scheduled if nothing changed since the last successful scheduled run | always: true |
| The YAML schedule never runs | Schedules set in the pipeline settings UI take precedence: only those run | Delete the UI schedules, then push a change |
| One branch runs, another doesn't | A branch gets scheduled runs only if it matches the filters in the YAML file in that branch | Add it to that branch's own filters |
| Runs at the wrong hour | Cron is UTC and doesn't account for daylight saving time | Convert local time to UTC |
| It also runs on every push | YAML pipelines in a GitHub repository have CI and PR triggers on by default | trigger: none and pr: none |
| Runs stop appearing | Limits of around 1000 runs per pipeline per week and 10 per 15 minutes | Schedule less often |
Where should the API key live?
In a secret variable, never in the YAML. Microsoft recommends setting secrets in the pipeline UI, in a variable group, or in a variable group linked to Azure Key Vault, and Sume's Authentication page lists CI secret stores among the places a key may live. Secrets are not decrypted into environment variables for scripts, so map the variable in the step's env:. Microsoft also says never to pass secrets on the command line, because some operating systems log command-line arguments; the example pipes the header into curl instead.
What happens if the pipeline runs twice?
Because the key comes from the date, a repeat on the same day replays the first run: Sume answers the same key and body with 200, the original receipt and idempotency_hit: true, so nothing is charged twice. Repeats happen: a manual run that day, or retryCountOnTaskFailure, which retries a failed step up to 10 times with growing waits and, in Microsoft's words, doesn't provide idempotency. Idempotency keys for AI video APIs covers the other replay cases, and a run that ended failed needs a new key before a same-day re-run, as Sume Format run failed explains. In this pipeline:
- Keep the day's
inputandwebhook_urlfixed. The same key with a different body is409 idempotency_conflict, and nothing runs. --fail-with-bodyfails the step on every4xx, so the step retry also resends answers that can't change, such as a402that needs funds first. Those resends are free: a4xxat create means nothing ran and nothing was charged.
Should the pipeline wait for the video?
No. Sume answers the create at once with a receipt, while long-form video is 15 to 30 minutes of work and a run can last until 90 minutes after creation before it is finalized as failed. A job's timeoutInMinutes defaults to 60, and on Microsoft-hosted agents a private project's job can't run past 60 minutes unless extra capacity is paid for, so a polling job may be canceled first. Abandoning the wait does not stop the run or its spend.
Send communication.webhook_url, as the example does, and end the job after the create: Sume POSTs one signed format.run.terminal receipt to your server when the run completes or fails. Signed webhooks for Sume video runs covers the receiving side.
Sources
- Create a run
- Runs and results
- Errors and spend
- Authentication
- Microsoft Learn: Configure schedules to run pipelines (read 2026-09-28)
- Microsoft Learn: Set secret variables (read 2026-09-28)
- Microsoft Learn: Jobs in Azure Pipelines (read 2026-09-28)
- Microsoft Learn: Task types and usage (read 2026-09-28)
- Microsoft Learn: steps.bash definition (read 2026-09-28)
- Microsoft Learn: Run and build numbers (read 2026-09-28)
- curl man page (read 2026-09-28)
Related posts
More in Integrations
- BullMQ retry: exponential backoff for paid API jobs
Set attempts and an exponential backoff on BullMQ jobs, stop early with UnrecoverableError, and key each paid API call to the job so retries replay.
- Celery task retry: backoff and jitter for a paid API call
Retry a Celery task with autoretry_for, retry_backoff and max_retries, wait out retry-after on a 429, and send one Idempotency-Key on every retry.
- Claude Code: allow MCP tools without approving every call
Allow MCP tools in Claude Code with permission rules named mcp__server__tool. Allow one tool or a whole server; keep paid tools on ask.
- Claude Code MCP project scope: share a server with your team
Claude Code's project scope saves an MCP server to .mcp.json at the repo root for the team to commit. How approval, sign-in, and keys work.
Written by Sume