# Update cron job

`PATCH https://api.instacloud.com/projects/{projectId}/cron-jobs/{id}`

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

Update a cron job. Requires If-Match: <revision>. Gated — cron.write.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `projectId` | path | `string` | yes |  |
| `id` | path | `string` | yes |  |
| `if-match` | header | `string` | yes | the job’s current `revision` |

## Request body (application/json)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | no |  |
| `expression` | `string` | no |  |
| `enabled` | `boolean` | no | false pauses the job; in-flight runs are blocked at the send boundary |
| `target` | `object` | no | Where 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. |
| `request` | `object` | no | Stored 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"` | no | default GET |
| `request.headers` | `object` | no | literal headers; put credentials in secretRefs instead |
| `request.body` | `string` | no | request body (POST only) |
| `request.secretRefs` | `object` | no | header 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`. |
| `requestTimeoutMs` | `integer` | no |  |
| `retry` | `object` | no |  |
| `retry.platformMaxRetries` | `integer` | no | retries for failures where nothing was sent (wake/transport). Default 2. |
| `retry.applicationMaxRetries` | `integer` | no | retries for a non-2xx answer. Default 0 — a broken endpoint should not be hammered. |
| `retry.retryableStatuses` | `integer[]` | no | e.g. [429, 503]; empty means none |
| `retry.maxRunAgeMs` | `integer` | no | give up on the run once it is this old, whatever the retry budget says |

## Response 200

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `job` | `object` | yes | A cron schedule. Header values are write-only; `request` describes the stored request without them. |
| `job.id` | `string` | yes |  |
| `job.project_id` | `string` | yes |  |
| `job.branch_id` | `string` | yes |  |
| `job.branch` | `string` | yes | branch name, joined for readability — the id is the identity |
| `job.name` | `string` | yes |  |
| `job.expression` | `string` | yes |  |
| `job.timezone` | `string` | yes | UTC in v1, so preview and the materializer can never disagree |
| `job.enabled` | `boolean` | yes |  |
| `job.revision` | `integer` | yes | the If-Match guard for PATCH |
| `job.next_run_at` | `string \| null` | yes |  |
| `job.target` | `object` | yes | Where 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.request` | `object` | yes | The non-secret description of the job’s request. |
| `job.request.method` | `"GET" \| "POST"` | yes |  |
| `job.request.headerNames` | `string[]` | yes | names only, literal and secret-backed alike — never values |
| `job.request.secretRefs` | `object` | yes | header 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_ms` | `integer` | yes |  |
| `job.retry_policy` | `object` | yes |  |
| `job.retry_policy.platformMaxRetries` | `integer` | no | retries for failures where nothing was sent (wake/transport). Default 2. |
| `job.retry_policy.applicationMaxRetries` | `integer` | no | retries for a non-2xx answer. Default 0 — a broken endpoint should not be hammered. |
| `job.retry_policy.retryableStatuses` | `integer[]` | no | e.g. [429, 503]; empty means none |
| `job.retry_policy.maxRunAgeMs` | `integer` | no | give up on the run once it is this old, whatever the retry budget says |
| `job.created_at` | `string` | yes |  |
| `job.updated_at` | `string` | yes |  |

## Response 202

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `status` | `"approval_required"` | no |  |
| `approvalId` | `string` | no |  |
| `action` | `string` | no | the gated action, or a comma-joined compound set — prefer `actions` |
| `actions` | `string[]` | no | every capability this approval covers |
| `message` | `string` | no |  |
| `url` | `string` | no | the console page where a project admin reviews this request |
| `nextActions` | `object[]` | no |  |
| `nextActions.op` | `string` | yes | Neutral 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.reason` | `string` | yes | Natural-language, human/LLM-facing "why do this now". |
| `nextActions.args` | `object` | no | Suggested, flat named arguments; "<placeholder>"s where a value is unknown. |
| `nextActions.gated` | `boolean` | no | True if the action passes a governance gate. |

## Response 400

missing/invalid If-Match, an invalid expression or target, or a new target without `request` on a POST job or one that sends headers

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 403

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 404

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 409

the job changed since that revision — re-read it and retry; or code service_public_access_closed: the new target compute service has public access closed

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |
| `code` | `string` | no | stable machine-readable reason, e.g. private_lane_disabled, private_lane_unavailable_in_region, public_access_closed, credential_change_in_progress |
