Kubernetes CronJob that submits a nightly 30-second Wan 3.0 clip
A CronJob manifest using curlimages/curl and a Secret: one dated Idempotency-Key per night, concurrency forbidden, and a month of reserves at each resolution.

A Kubernetes CronJob can submit a nightly 30-second Wan 3.0 clip with nothing but curl: the container posts to /v1/videos, sends an Idempotency-Key containing the date, and exits. A pod that Kubernetes restarts then cannot create a second job for the same night. Each 720p run reserves $3.75.
The manifest
Create the Secret first: kubectl create secret generic sume --from-literal=api-key=.... $$(date ...) is how Kubernetes passes a literal $(date ...) to the shell.
apiVersion: batch/v1
kind: CronJob
metadata: { name: wan-nightly-30s }
spec:
schedule: "0 6 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: submit
image: curlimages/curl:8.10.1
env:
- name: SUME_API_KEY
valueFrom: { secretKeyRef: { name: sume, key: api-key } }
command: ["sh", "-c"]
args:
- |
curl -fsS -X POST https://api.sume.com/v1/videos \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: nightly-$$(date -u +%F)" \
-d '{"model":"wan-3.0","prompt":"Time-lapse of a city street from dusk to night","duration":30,"resolution":"720p","aspect_ratio":"16:9","callback_url":"https://hooks.example.com/sume"}'Why these settings
concurrencyPolicy: ForbidandbackoffLimit: 0keep a slow night from stacking submits. The dated key is the real guard: the same date means the same job.curl -fmakes HTTP errors a non-zero exit, so a402 insufficient_creditsshows up as a failed Job inkubectl get jobs.callback_urlmust be HTTPS. The receiver should verify the signature headers described in Webhooks and refuse to run with an empty secret.
Thirty nights of reserves
One run a day for 30 days, 30 seconds each, at the three resolutions:
| Resolution | Per night | 30 nights |
|---|---|---|
| 480p | $1.875 | $56.25 |
| 720p | $3.75 | $112.50 |
| 1080p | $7.50 | $225.00 |
Reading the outcome
The container logs the submit response, which contains the job id and polling_url. With callback_url set, your receiver gets the completion event; without it, poll the polling_url from the submit response. A failed submit exits non-zero because of curl -f, and kubectl logs job/<name> shows the error body.
Keep the Secret in the same namespace as the CronJob, rotate it by updating the Secret rather than the manifest, and never echo $SUME_API_KEY in the args.
Keep the balance ahead of the schedule
Sume reserves the price at submit and fails the call with 402 insufficient_credits if the balance cannot cover it. A nightly job therefore needs at least one night's reserve in the balance at 06:00; set a reminder when it drops below a week's worth.
Sources
Related posts
More in Developers
- List your Sume Formats in Python and keep the video ones
Page through GET /v1/formats with next_cursor, keep io.output_kind video, and print each vanity_invoke_url. A runnable snippet with field caveats.
- MAI-Voice-2.1 SSML style="happiness" is not in the voice style list
Microsoft's MAI-Voice-2.1 SSML example uses style="happiness", but the Learn page's own style lists say happy or joyful. Check styles before you ship.
- Modal 1.6.1 endpoint logs and stats: debug a Sume webhook receiver
Modal 1.6.1 adds modal endpoint info, stats and logs. Use them to see why a Sume job webhook got a 401 or a timeout, and check the 150 s web timeout first.
- model sume/auto on /v1/videos: defaults, limits, replay-stable price
sume/auto lets Sume pick the video family. Defaults are 720p and 8 s, clips run 3 to 10 s at 16:9 or 9:16, and a replay gets the same route and price.
Written by Sume