Schedule Management
Section titled “Schedule Management”The Schedule Management endpoints let you list every @schedule directive registered in a container, manually trigger a scheduled fire, rescan the scripts directory to pick up directive changes, and inspect the historical log of fire attempts. Use these operations to monitor cron-like behavior, recover from a corrupted registration set, or run an on-demand job without waiting for the next cron tick.
All endpoints are mounted under the container-scoped exec service. The base URL follows the pattern https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.icu.
List Schedules
Section titled “List Schedules”Returns every currently registered @schedule directive along with the computed next fire timestamp and the most recent fire metadata. The list reflects the in-memory registration set, refreshed on boot and after every reload.
GET /api/v1/exec/schedules/list
curl -X GET "https://proj-abc-cnt-xyz-exec-1.us-east.containers.hoody.icu/api/v1/exec/schedules/list" \ -H "Authorization: Bearer <token>"await client.exec.schedules.listSchedules();{ "total": 2, "schedules": [ { "scriptPath": "/srv/scripts/default/nightly-cleanup.ts", "scriptRel": "default/nightly-cleanup.ts", "subdomain": "default", "execId": "default", "vmCacheKey": "shared:default", "expression": "@daily", "timeoutMs": 300000, "registeredAt": "2025-01-12T08:00:00.000Z", "nextFire": "2025-01-13T00:00:00.000Z", "lastFireAt": "2025-01-12T00:00:04.812Z", "lastFireStatus": "ok", "lastFireRunId": "8f4a7c2e-1b3d-4d5e-9f0a-2c3b4d5e6f70" }, { "scriptPath": "/srv/scripts/api/hourly-rollup.ts", "scriptRel": "api/hourly-rollup.ts", "subdomain": "api", "vmCacheKey": "shared:api", "expression": "0 * * * *", "timeoutMs": 0, "registeredAt": "2025-01-10T12:30:11.140Z", "nextFire": "2025-01-13T09:00:00.000Z", "lastFireAt": "2025-01-13T08:00:07.221Z", "lastFireStatus": "error", "lastFireRunId": "9c1b8e3f-7a2c-4e1b-8f9d-3a4b5c6d7e81" } ]}Response Statuses
Section titled “Response Statuses”Success. The body contains the registration snapshot.
{ "error": "Invalid query parameters", "code": "VALIDATION_ERROR", "timestamp": "2025-01-13T09:14:22.108Z", "details": { "field": "limit", "reason": "must be a positive integer" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-13T09:14:22.108Z"}{ "error": "Scheduler not initialized", "code": "ERROR_503", "timestamp": "2025-01-13T09:14:22.108Z"}Trigger Schedule
Section titled “Trigger Schedule”Fires a registered @schedule script on demand and waits for the run to complete. The response includes the actual fire outcome — the request can succeed while the underlying fire errored or timed out.
POST /api/v1/exec/schedules/trigger
This endpoint takes no path, query, or header parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
scriptPath | string | Yes | Script path (absolute or relative to scripts-dir) of a script with a valid @schedule directive. |
force | boolean | No | When true, bypass the @token refusal. Use with care — this fires the script as cron (no token auth). Default: false. |
curl -X POST "https://proj-abc-cnt-xyz-exec-1.us-east.containers.hoody.icu/api/v1/exec/schedules/trigger" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "scriptPath": "default/nightly-cleanup.ts" }'await client.exec.schedules.triggerSchedule({ scriptPath: "default/nightly-cleanup.ts"});{ "triggered": true, "scriptPath": "default/nightly-cleanup.ts", "runId": "2a4f6b81-3c5d-4e7f-9a1b-2c3d4e5f6071", "status": "ok", "durationMs": 1842}Response Statuses
Section titled “Response Statuses”Success. The fire ran to completion (or was intentionally skipped). Check status and triggered for the actual outcome.
{ "error": "Missing required field: scriptPath", "code": "VALIDATION_ERROR", "timestamp": "2025-01-13T09:14:22.108Z", "details": { "field": "scriptPath" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Trigger is forbidden on scripts guarded by @token unless force=true", "code": "FORBIDDEN", "timestamp": "2025-01-13T09:14:22.108Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
FORBIDDEN | Access denied | Insufficient permissions for this operation | Contact administrator for access |
{ "error": "No schedule registered for script 'default/missing.ts'", "code": "NOT_FOUND", "timestamp": "2025-01-13T09:14:22.108Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
NOT_FOUND | Resource not found | The requested resource does not exist | Verify the resource identifier |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-13T09:14:22.108Z"}{ "error": "Scheduler not initialized", "code": "ERROR_503", "timestamp": "2025-01-13T09:14:22.108Z"}Reload Schedules
Section titled “Reload Schedules”Rescans the scripts directory, computes the diff against the current registration set, and applies it. Use dry_run: true to preview which scripts will be added, kept, or removed without touching the live schedule.
POST /api/v1/exec/schedules/reload
This endpoint takes no path, query, or header parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
dry_run | boolean | No | When true, compute the diff against the filesystem but do not apply. Returns the same shape with {added, kept, removed} lists. Default: false. |
curl -X POST "https://proj-abc-cnt-xyz-exec-1.us-east.containers.hoody.icu/api/v1/exec/schedules/reload" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "dry_run": true }'await client.exec.schedules.reloadSchedules({ dry_run: true});{ "dry_run": true, "added": ["api/hourly-rollup.ts"], "kept": ["default/nightly-cleanup.ts"], "removed": ["legacy/old-job.ts"], "failed": [ { "path": "default/broken-cron.ts", "reason": "Invalid cron expression: 'every 5 minutes'" } ]}Response Statuses
Section titled “Response Statuses”Success. The diff (or the applied changes) is returned in the body. failed entries appear in both dry_run and apply responses.
{ "error": "Invalid payload: dry_run must be a boolean", "code": "VALIDATION_ERROR", "timestamp": "2025-01-13T09:14:22.108Z"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-13T09:14:22.108Z"}{ "error": "Scheduler not initialized", "code": "ERROR_503", "timestamp": "2025-01-13T09:14:22.108Z"}Schedule History
Section titled “Schedule History”Returns newest-first NDJSON entries from fires.log, one row per fire completion. Each entry records the wall-clock duration, status, and (when present) a truncated result preview or error message. Use includeRotated: true to also scan rotated fires.log.* files at the cost of a slower query.
GET /api/v1/exec/schedules/history
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
scriptPath | query | string | No | Filter entries to a specific script (relative to scripts-dir). Optional. |
since | query | string | No | ISO 8601 lower bound on ts. Optional. |
limit | query | integer | No | Max entries to return. Default: 100, hard max 1000. |
includeRotated | query | boolean | No | When true, also scan rotated fires.log.* files (slower). Default: false. |
curl -X GET "https://proj-abc-cnt-xyz-exec-1.us-east.containers.hoody.icu/api/v1/exec/schedules/history?scriptPath=default%2Fnightly-cleanup.ts&limit=50" \ -H "Authorization: Bearer <token>"await client.exec.schedules.scheduleHistory({ scriptPath: "default/nightly-cleanup.ts", limit: 50});{ "total": 3, "limit": 50, "includeRotated": false, "entries": [ { "ts": "2025-01-13T00:00:04.812Z", "scriptPath": "default/nightly-cleanup.ts", "expression": "@daily", "runId": "8f4a7c2e-1b3d-4d5e-9f0a-2c3b4d5e6f70", "status": "ok", "durationMs": 1842, "returnPreview": "{\"deleted\":1402,\"retentionDays\":30}" }, { "ts": "2025-01-12T00:00:05.011Z", "scriptPath": "default/nightly-cleanup.ts", "expression": "@daily", "runId": "7e3d9a44-6f1c-4b8e-8d2a-1c2d3e4f5062", "status": "error", "durationMs": 2093, "error": "Database connection refused: ECONNREFUSED 10.0.0.42:5432" }, { "ts": "2025-01-11T08:30:00.140Z", "scriptPath": "api/hourly-rollup.ts", "expression": "0 * * * *", "runId": "5b2c1a93-7d4e-4f8b-9c0d-2e3f4a5b6c71", "status": "skipped_overload", "durationMs": 0, "error": "Inflight cap of 4 reached; skipping fire" } ]}Response Statuses
Section titled “Response Statuses”Success. The body contains the requested window of fire entries, newest first.
{ "error": "Invalid 'limit': must be between 1 and 1000", "code": "VALIDATION_ERROR", "timestamp": "2025-01-13T09:14:22.108Z", "details": { "field": "limit" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "Internal server error", "code": "ERROR_500", "timestamp": "2025-01-13T09:14:22.108Z"}{ "error": "Scheduler not initialized", "code": "ERROR_503", "timestamp": "2025-01-13T09:14:22.108Z"}