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
- AWS Lambda's 3 s default vs POST /v1/images' 30 s sync wait
POST /v1/images waits up to 30 seconds by default, but a Lambda defaults to 3. Send mode async with an Idempotency-Key and return the job id instead.
- Lambda get_remaining_time_in_millis: stop polling a Sume job in time
A Lambda that polls a Sume job should check get_remaining_time_in_millis before each sleep and return the job id so a later invocation can resume. Python.
- Largest square GPT Image 2.5 takes on Sume: 2880x2880
2880x2880 is exactly 8,294,400 pixels, the top of the GPT Image 2.5 custom-size box on Sume. 2896x2896 is over. 4K UHD lands on the same cap.
- 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.
Written by Sume