Documentation menu

Create cron job

POSThttps://api.instacloud.com/projects/{projectId}/cron-jobs

Token scope: account · org · project — read-only tokens refused (not a GET).

Create a cron job on a branch. Gated — cron.write. 403 also when the org is billing-suspended, its schedule quota is full, or the target service belongs to another organization.

Parameters

NameInTypeRequiredDescription
projectIdpathstringyes

Request body

application/json

FieldTypeRequiredDescription
namestringyesunique per branch
expressionstringyes5-field cron expression, always evaluated in UTC
branchstringnotarget branch (default: the project’s default branch)
targetobjectyesWhere the job sends its request. An internal target names the SERVICE, never a URL: the worker resolves the live route at send time, so a redeploy or a region move cannot leave a job firing at a dead host.
requestobjectnoStored encrypted; its VALUES are never returned. Send the whole object to replace it — reads answer with `request` (method and header names only).
request.method"GET" | "POST"nodefault GET
request.headersobjectnoliteral headers; put credentials in secretRefs instead
request.bodystringnorequest body (POST only)
request.secretRefsobjectnoheader name → project secret NAME. Resolved per attempt from whatever is current, so rotating a secret takes effect on the next attempt with no edit to the job. Looked up in the JOB’S own branch, then its project — not the target’s — so a service in another project or branch of this org sees a different scope than its own env; for a service target IN THE SAME project and branch, secrets bound to that service also count (the same value the service reads from its env there). Only user-defined secrets resolve — platform-managed credentials such as DATABASE_URL never do. A header may not be in both `headers` and `secretRefs`.
requestTimeoutMsintegernoexcludes cold start: the wake is timed separately
retryobjectno
retry.platformMaxRetriesintegernoretries for failures where nothing was sent (wake/transport). Default 2.
retry.applicationMaxRetriesintegernoretries for a non-2xx answer. Default 0 — a broken endpoint should not be hammered.
retry.retryableStatusesinteger[]noe.g. [429, 503]; empty means none
retry.maxRunAgeMsintegernogive up on the run once it is this old, whatever the retry budget says

Responses

201

Default Response

FieldTypeRequiredDescription
jobobjectyesA cron schedule. Header values are write-only; `request` describes the stored request without them.
job.idstringyes
job.project_idstringyes
job.branch_idstringyes
job.branchstringyesbranch name, joined for readability — the id is the identity
job.namestringyes
job.expressionstringyes
job.timezonestringyesUTC in v1, so preview and the materializer can never disagree
job.enabledbooleanyes
job.revisionintegeryesthe If-Match guard for PATCH
job.next_run_atstring | nullyes
job.targetobjectyesWhere the job sends its request. An internal target names the SERVICE, never a URL: the worker resolves the live route at send time, so a redeploy or a region move cannot leave a job firing at a dead host.
job.requestobjectyesThe non-secret description of the job’s request.
job.request.method"GET" | "POST"yes
job.request.headerNamesstring[]yesnames only, literal and secret-backed alike — never values
job.request.secretRefsobjectyesheader name → secret NAME for the secret-backed headers. Names are not credentials, so they are returned: an edit can re-send these as they are, where a literal header’s value must be supplied again.
job.request_timeout_msintegeryes
job.retry_policyobjectyes
job.retry_policy.platformMaxRetriesintegernoretries for failures where nothing was sent (wake/transport). Default 2.
job.retry_policy.applicationMaxRetriesintegernoretries for a non-2xx answer. Default 0 — a broken endpoint should not be hammered.
job.retry_policy.retryableStatusesinteger[]noe.g. [429, 503]; empty means none
job.retry_policy.maxRunAgeMsintegernogive up on the run once it is this old, whatever the retry budget says
job.created_atstringyes
job.updated_atstringyes

202

Default Response

FieldTypeRequiredDescription
status"approval_required"no
approvalIdstringno
actionstringnothe gated action, or a comma-joined compound set — prefer `actions`
actionsstring[]noevery capability this approval covers
messagestringno
urlstringnothe console page where a project admin reviews this request
nextActionsobject[]no
nextActions.opstringyesNeutral logical action id, e.g. "service.add" — NOT an operationId or a CLI/MCP tool name; each client maps it to its own surface.
nextActions.reasonstringyesNatural-language, human/LLM-facing "why do this now".
nextActions.argsobjectnoSuggested, flat named arguments; "<placeholder>"s where a value is unknown.
nextActions.gatedbooleannoTrue if the action passes a governance gate.

400

invalid expression, target or name

FieldTypeRequiredDescription
errorstringyes

403

Default Response

FieldTypeRequiredDescription
errorstringyes

404

Default Response

FieldTypeRequiredDescription
errorstringyes

409

a job of this name already exists on the branch; or code service_public_access_closed: the target compute service has public access closed, so the job could not reach it

FieldTypeRequiredDescription
errorstringyes
codestringnostable machine-readable reason, e.g. private_lane_disabled, private_lane_unavailable_in_region, public_access_closed, credential_change_in_progress

View as Markdown