Docs
BlogHomeStart building

Scheduled Jobs

Run your app's work on a schedule.


A scheduled job calls one of the app's own routes (for example POST /api/cron/daily-report) on a schedule. A job belongs to one environment: a dev job calls the development preview, a live job calls the published site. Create dev jobs while building: publishing creates their live twins and every later publish carries the dev job's changes (schedule, address, time zone, on/off) to its twin and deletes the twin of a deleted dev job. live jobs you create yourself are never touched by a publish. The app can also create jobs itself with totalum.cron (SDK).

PlanJobs per environmentShortest interval
Free21 hour
Starter101 minute
Builder, Professional, Enterprise201 minute

Cost. Creating and keeping jobs is free. Each run costs about 0.0004 credits for the scheduling itself, plus what the request costs like any visit (its compute time, its database rows, any built-in feature it calls). A light job every hour costs about 0.36 credits a month; every minute, about 18.6. A failed run (an answer that is not 2xx, or no answer in time) counts toward pauseAfterFailures; the owner is told by e-mail and the cron.job.paused / cron.job.failing webhooks.

#List Cron Jobs

GET/api/v1/vcaas/projects/:projectId/cron-jobsapi-keyFree

The project's jobs, both environments.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.itemsobject[]The jobs
data.items[].idstringJob id
data.items[].namestringName
data.items[].schedulestring | objectA 5-field cron expression, or { everyMinutes }
data.items[].timezonestring | undefinedIANA time zone of the schedule (absent = UTC)
data.items[].requestobject{ method, url, headers?, body?, timeoutMs? }
data.items[].envstring"dev" or "live"
data.items[].enabledbooleanSwitched on
data.items[].statusstring"active", "paused" or "paused_failures" (paused after too many failures)
data.items[].nextRunAtstring | nullNext run
data.items[].lastRunobject | undefined{ at, status, latencyMs }; status is the HTTP status, or "timeout", "error", "skipped"
data.items[].pauseAfterFailuresnumber | nullFailures in a row that pause it; null = never
data.items[].descriptionstring | undefinedDescription
data.items[].createdAtstringISO 8601

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/cron-jobs
Success · 200 OK
json
{ "errors": null, "data": { "items": [{ "id": "cj_01k6xv", "name": "daily-report", "schedule": "0 9 * * *", "timezone": "Europe/Madrid", "request": { "method": "POST", "url": "https://barber-bookings.totalum-project.com/api/cron/daily-report" }, "env": "live", "enabled": true, "status": "active", "nextRunAt": "2026-10-09T07:00:00.000Z", "pauseAfterFailures": 3, "createdAt": "2026-10-08T12:00:00.000Z" }] } }

#Create Cron Job

POST/api/v1/vcaas/projects/:projectId/cron-jobsapi-keyFree

Create a job. The URL must be on the environment's own host: the preview address for dev, the published <projectId>.totalum-project.com for live (which needs a published project). Without env the job belongs to the environment of its URL: the preview address makes a dev job (so an unpublished project needs no env), any other address a live one.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Body parameters

FieldTypeRequiredDescription
namestringYes1–64 characters
schedulestring | objectYesA 5-field cron expression ("*/15 * * * *"), or { "everyMinutes": 30 }
requestobjectYesWhat to call
request.methodstringYes"GET" or "POST"
request.urlstringYeshttps:// URL on the environment's host
request.headersobjectNoUp to 10 headers; a value may be {{secret:NAME}} to send a project secret
request.bodystringNoUp to 64 KB
request.timeoutMsnumberNoUp to 30,000
envstringNo"dev" or "live" (default: the environment of request.url — "dev" for the preview address, else "live")
timezonestringNoIANA zone, e.g. "Europe/Madrid": "0 9 * * *" runs at 09:00 there all year. Default UTC
pauseAfterFailuresnumber | nullNo1–100 failures in a row pause the job (default 3); null or 0 = never pause
enabledbooleanNoDefault true
descriptionstringNoFree text

Response fields

FieldTypeDescription
dataobjectThe job, with the fields of items[] in List Cron Jobs

Example request

curl -X POST \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"name":"daily-report","env":"dev","schedule":"0 9 * * *","timezone":"Europe/Madrid","request":{"method":"POST","url":"https://barber-bookings-dev-temporal-link-preview-k3y9x2ab.totalum-project.com/api/cron/daily-report"}}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/cron-jobs
Success · 201 Created
json
{ "errors": null, "data": { "id": "cj_01k6xw", "name": "daily-report", "schedule": "0 9 * * *", "timezone": "Europe/Madrid", "request": { "method": "POST", "url": "https://barber-bookings-dev-temporal-link-preview-k3y9x2ab.totalum-project.com/api/cron/daily-report" }, "env": "dev", "enabled": true, "status": "active", "nextRunAt": "2026-10-09T07:00:00.000Z", "pauseAfterFailures": 3, "createdAt": "2026-10-08T12:00:00.000Z" } }

Error codes

CodeHTTPMeaning
INVALID_SCHEDULE400Invalid, or more frequent than your plan allows (errorDetails.minIntervalSeconds)
VALIDATION_ERROR400A field is invalid (an unknown timezone, pauseAfterFailures out of range…)
CRON_URL_NOT_ALLOWED403The URL is not on the environment's host (errorDetails.allowedHost)
CRON_REQUIRES_PUBLISHED_PROJECT403A live job on a project that is not published
CRON_LIMIT_REACHED429The environment already has your plan's number of jobs (errorDetails.limit)
End of Create Cron JobNext endpointGETGet Cron Job

#Get Cron Job

GET/api/v1/vcaas/projects/:projectId/cron-jobs/:jobIdapi-keyFree

One job.

Path parameters

ParameterTypeDescription
projectIdstringThe project id
jobIdstringThe job id

Response fields

FieldTypeDescription
dataobjectThe job, as in List Cron Jobs

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/cron-jobs/cj_01k6xw
Success · 200 OK
json
{ "errors": null, "data": { "id": "cj_01k6xw", "name": "daily-report", "schedule": "0 9 * * *", "request": { "method": "POST", "url": "https://barber-bookings-dev-temporal-link-preview-k3y9x2ab.totalum-project.com/api/cron/daily-report" }, "env": "dev", "enabled": true, "status": "active", "nextRunAt": "2026-10-09T09:00:00.000Z", "pauseAfterFailures": 3, "createdAt": "2026-10-08T12:00:00.000Z" } }

Error codes

CodeHTTPMeaning
CRON_JOB_NOT_FOUND404No such job

#Update Cron Job

PATCH/api/v1/vcaas/projects/:projectId/cron-jobs/:jobIdapi-keyFree

Change any field of a job, or switch it on or off (enabled). A paused job runs again once it is enabled.

Path parameters

ParameterTypeDescription
projectIdstringThe project id
jobIdstringThe job id

Body parameters

FieldTypeRequiredDescription
name, schedule, request, env, timezone, pauseAfterFailures, enabled, descriptionNoAny field of Create Cron Job

Response fields

FieldTypeDescription
dataobjectThe job after the change

Example request

curl -X PATCH \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"enabled":false}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/cron-jobs/cj_01k6xw
Success · 200 OK
json
{ "errors": null, "data": { "id": "cj_01k6xw", "name": "daily-report", "schedule": "0 9 * * *", "request": { "method": "POST", "url": "https://barber-bookings-dev-temporal-link-preview-k3y9x2ab.totalum-project.com/api/cron/daily-report" }, "env": "dev", "enabled": false, "status": "paused", "nextRunAt": null, "pauseAfterFailures": 3, "createdAt": "2026-10-08T12:00:00.000Z" } }

Error codes

CodeHTTPMeaning
CRON_JOB_NOT_FOUND404No such job
INVALID_SCHEDULE400Invalid or too frequent schedule
CRON_URL_NOT_ALLOWED403The URL is not on the environment's host

#Delete Cron Job

DELETE/api/v1/vcaas/projects/:projectId/cron-jobs/:jobIdapi-keyFree

Delete a job.

Path parameters

ParameterTypeDescription
projectIdstringThe project id
jobIdstringThe job id

Response fields

FieldTypeDescription
data.deletedbooleantrue

Example request

curl -X DELETE -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/cron-jobs/cj_01k6xw
Success · 200 OK
json
{ "errors": null, "data": { "deleted": true } }

Error codes

CodeHTTPMeaning
CRON_JOB_NOT_FOUND404No such job
End of Delete Cron JobNext endpointPOSTRun Cron Job

#Run Cron Job

POST/api/v1/vcaas/projects/:projectId/cron-jobs/:jobId/runapi-keyFree

Run a job now, once. The run appears in its executions. At most 10 manual runs a minute per project.

Path parameters

ParameterTypeDescription
projectIdstringThe project id
jobIdstringThe job id

Response fields

FieldTypeDescription
data.runIdstringThe execution

Example request

curl -X POST -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/cron-jobs/cj_01k6xw/run
Success · 200 OK
json
{ "errors": null, "data": { "runId": "cr_01k6xx" } }

Error codes

CodeHTTPMeaning
CRON_JOB_NOT_FOUND404No such job
RATE_LIMITED429More than 10 manual runs in a minute; retry after errorDetails.retryAfterSeconds

#List Cron Job Executions

GET/api/v1/vcaas/projects/:projectId/cron-jobs/:jobId/executionsapi-keyFree

A job's executions of the last 30 days, newest first.

Path parameters

ParameterTypeDescription
projectIdstringThe project id
jobIdstringThe job id

Query parameters

FieldTypeRequiredDescription
limitnumberNoPage size
cursorstringNonextCursor of the previous page

Response fields

FieldTypeDescription
data.items[].idstringExecution id
data.items[].scheduledForstringWhen it was due
data.items[].startedAtstring | nullWhen it ran
data.items[].statusnumber | stringHTTP status, or "timeout", "error", "skipped" (not run: no credits, or the cron feature is off or over budget)
data.items[].latencyMsnumber | nullResponse time
data.items[].errorstring | undefinedWhy it failed or was skipped
data.items[].responseSnippetstring | undefinedThe first 1 KB of the answer
data.nextCursorstring | nullNext page

Example request

curl -H "api-key: tlm_sk_your_key" \
  "https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/cron-jobs/cj_01k6xw/executions?limit=20"
Success · 200 OK
json
{ "errors": null, "data": { "items": [{ "id": "cr_01k6xx", "scheduledFor": "2026-10-08T12:05:00.000Z", "startedAt": "2026-10-08T12:05:00.000Z", "status": 200, "latencyMs": 142, "responseSnippet": "{\"ok\":true}" }], "nextCursor": null } }

Error codes

CodeHTTPMeaning
CRON_JOB_NOT_FOUND404No such job
End of List Cron Job Executions Back to top