Schedule Management
Section titled “Schedule Management”The scheduling service registers @schedule directives found in scripts under /hoody/storage/hoody-exec/scripts, fires them on a cron expression in UTC, and records every fire in fires.log. Use these endpoints to inspect what is currently scheduled, reload registrations after a script change, trigger a fire on demand, and read historical outcomes.
All requests target the exec container hostname and require a Bearer token.
List & Inspect
Section titled “List & Inspect”GET /api/v1/exec/schedules/list
Section titled “GET /api/v1/exec/schedules/list”Return every currently registered @schedule directive with its computed next fire and last fire summary. Use this to see what the scheduler is currently tracking after boot or after a reload.
This endpoint takes no parameters.
curl -X GET "https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/schedules/list" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.schedules.listSchedules();{ "total": 2, "schedules": [ { "scriptPath": "/hoody/storage/hoody-exec/scripts/default/1/cron/nightly-report.ts", "scriptRel": "default/1/cron/nightly-report.ts", "subdomain": "default", "execId": "1", "vmCacheKey": "default", "expression": "0 2 * * *", "timeoutMs": 300000, "registeredAt": "2026-01-15T08:00:00.000Z", "nextFire": "2026-01-16T02:00:00.000Z", "lastFireAt": "2026-01-15T02:00:04.812Z", "lastFireStatus": "ok", "lastFireRunId": "f7d2a1c0-9b3a-4d6e-8c2f-1e0a3b4c5d6e" }, { "scriptPath": "/hoody/storage/hoody-exec/scripts/default/1/cron/hourly-cleanup.ts", "scriptRel": "default/1/cron/hourly-cleanup.ts", "subdomain": "default", "execId": "1", "vmCacheKey": "default", "expression": "@hourly", "timeoutMs": 60000, "registeredAt": "2026-01-15T08:00:00.000Z", "nextFire": "2026-01-15T09:00:00.000Z", "lastFireAt": null, "lastFireStatus": null, "lastFireRunId": null } ]}{ "error": "VALIDATION_ERROR", "code": "ERROR_400", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}| 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": "2026-01-15T08:00:00.000Z", "details": {}}{ "error": "Service unavailable", "code": "ERROR_503", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}GET /api/v1/exec/schedules/history
Section titled “GET /api/v1/exec/schedules/history”Read past fire records from fires.log (newest-first). Use this to audit recent cron runs, debug a failure, or scan rotated files when investigating older 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://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/schedules/history?scriptPath=default/1/cron/nightly-report.ts&limit=50" \ -H "Authorization: Bearer <token>"import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.schedules.scheduleHistory({ scriptPath: 'default/1/cron/nightly-report.ts', limit: 50 });{ "total": 2, "limit": 50, "includeRotated": false, "entries": [ { "ts": "2026-01-15T02:00:04.812Z", "scriptPath": "default/1/cron/nightly-report.ts", "expression": "0 2 * * *", "runId": "f7d2a1c0-9b3a-4d6e-8c2f-1e0a3b4c5d6e", "status": "ok", "durationMs": 4812, "returnPreview": "{\"rows\":42,\"emailed\":true}" }, { "ts": "2026-01-14T02:00:03.991Z", "scriptPath": "default/1/cron/nightly-report.ts", "expression": "0 2 * * *", "runId": "b1c4d2e3-5a67-4f89-9b01-2c3d4e5f6a7b", "status": "error", "durationMs": 198, "error": "TypeError: Cannot read properties of undefined (reading 'rows')" } ]}{ "error": "VALIDATION_ERROR", "code": "ERROR_400", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}| 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": "2026-01-15T08:00:00.000Z", "details": {}}{ "error": "Service unavailable", "code": "ERROR_503", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}Trigger & Reload
Section titled “Trigger & Reload”POST /api/v1/exec/schedules/trigger
Section titled “POST /api/v1/exec/schedules/trigger”Fire a registered @schedule directive on demand without waiting for its next cron time. The script runs as if cron fired it, so it does not pass through @token checks unless force is set. Returns the actual outcome of the fire attempt, including skip reasons such as overload or stale generation.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Name | 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://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/schedules/trigger" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "scriptPath": "default/1/cron/nightly-report.ts" }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.schedules.triggerSchedule({ scriptPath: 'default/1/cron/nightly-report.ts' });{ "triggered": true, "scriptPath": "default/1/cron/nightly-report.ts", "runId": "c8e1d2a3-4b5f-4e7d-9c01-2a3b4c5d6e7f", "status": "ok", "durationMs": 4711}{ "error": "VALIDATION_ERROR", "code": "ERROR_400", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}| Error Code | Title | Description | Resolution |
|---|---|---|---|
VALIDATION_ERROR | Invalid input | Request parameters failed validation | Check parameter format and requirements |
{ "error": "FORBIDDEN", "code": "ERROR_403", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}| Error Code | Title | Description | Resolution |
|---|---|---|---|
FORBIDDEN | Access denied | Insufficient permissions for this operation | Contact administrator for access |
{ "error": "NOT_FOUND", "code": "ERROR_404", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}| 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": "2026-01-15T08:00:00.000Z", "details": {}}{ "error": "Service unavailable", "code": "ERROR_503", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}POST /api/v1/exec/schedules/reload
Section titled “POST /api/v1/exec/schedules/reload”Rescan the scripts directory, pick up new @schedule directives, drop registrations for deleted scripts, and refresh existing ones. Use this after editing a script’s @schedule line or after pulling new scripts into /hoody/storage/hoody-exec/scripts. Set dry_run: true to preview the diff before applying.
This endpoint takes no parameters.
Request Body
Section titled “Request Body”| Name | 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://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com/api/v1/exec/schedules/reload" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "dry_run": true }'import { HoodyClient } from 'hoody-sdk';
const client = new HoodyClient({ baseURL: 'https://{projectId}-{containerId}-exec-1.{server}.containers.hoody.com', token: process.env.HOODY_TOKEN });
await client.exec.schedules.reloadSchedules({ dry_run: true });{ "dry_run": true, "added": [ "default/1/cron/nightly-report.ts" ], "kept": [ "default/1/cron/hourly-cleanup.ts" ], "removed": [ "default/1/cron/legacy-export.ts" ], "failed": [ { "path": "default/1/cron/broken-cron.ts", "reason": "Invalid cron expression at line 3" } ]}{ "error": "VALIDATION_ERROR", "code": "ERROR_400", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}| 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": "2026-01-15T08:00:00.000Z", "details": {}}{ "error": "Service unavailable", "code": "ERROR_503", "timestamp": "2026-01-15T08:00:00.000Z", "details": {}}