Schedule
The schedule endpoint returns today’s optimized temperature plan for your HVAC system. Call it once daily to get 48 half-hour intervals covering the next 24 hours.
For multi-appliance schedules (HVAC + EV charger + battery + water heater in one call) use /api/v1/schedules.
GET /api/v1/schedule
Section titled “GET /api/v1/schedule”Auth: Required (Bearer JWT)
Example
Section titled “Example”curl https://api.hungrymachines.io/api/v1/schedule \ -H "Authorization: Bearer YOUR_TOKEN"Response (200) — optimized schedule
Section titled “Response (200) — optimized schedule”{ "date": "2025-11-18", "schedule": { "intervals": [0, 1, 2, "...", 47], "high_temps": [74.0, 74.0, "...(48 values)"], "low_temps": [70.0, 70.0, "...(48 values)"], "setpoint_temps": [72.0, 71.5, "...(48 values)"], "temp_trajectory": [72.1, 71.6, "...(48 values)"] }, "mode": "cool", "estimated_savings_pct": 18.5, "model_confidence": 0.23, "generated_at": "2025-11-18T04:15:00+00:00", "source": "optimization"}Response fields
Section titled “Response fields”| Field | Type | Description |
|---|---|---|
date | string | Schedule date (YYYY-MM-DD) |
schedule.intervals | int[48] | Always [0, 1, ..., 47] — 48 half-hour slots |
schedule.high_temps | float[48] | Per-interval upper comfort bound (°F). Display-only reference for charts; the thermostat does not see this value. |
schedule.low_temps | float[48] | Per-interval lower comfort bound (°F). Display-only — same as high_temps. |
schedule.setpoint_temps | float[48] | null | The 48-element command the integration applies to the thermostat at each 30-min boundary. Single source of truth for what to set, regardless of HVAC mode (cool, heat, auto). null on source: "defaults" rows and on legacy rows generated before this field existed. |
schedule.temp_trajectory | float[48] | null | Unclamped indoor-temperature forecast from the simulator for charting. null on defaults / legacy rows. |
mode | string | "cool", "heat", "auto", or "off" |
estimated_savings_pct | float | Estimated % savings vs. a naive fixed-setpoint thermostat |
model_confidence | float | null | R-squared of the fitted thermal model. null while the user is still on defaults. |
generated_at | string | When the optimization ran (UTC ISO 8601) |
stale | boolean | Only present (and true) when returning a prior day’s schedule because no run completed today |
source | string | "optimization" or "defaults" |
The 48-interval shape
Section titled “The 48-interval shape”Every schedule contains exactly 48 intervals, each a 30-minute window:
| Interval | Time |
|---|---|
| 0 | 00:00–00:30 |
| 1 | 00:30–01:00 |
| … | … |
| 16 | 08:00–08:30 |
| 34 | 17:00–17:30 |
| 47 | 23:30–00:00 |
Compute the current interval as interval = hour * 2 + (1 if minute >= 30 else 0). All timestamps in the API are UTC — convert to your local timezone before applying.
Response variants
Section titled “Response variants”Stale schedule
Section titled “Stale schedule”If no optimization ran today (e.g., the nightly job hasn’t fired yet, or a network blip prevented it), the most recent available schedule is returned with stale: true:
{ "date": "2025-11-17", "stale": true, "source": "optimization", "...": "..."}Defaults (warm-up period)
Section titled “Defaults (warm-up period)”If the optimizer hasn’t yet produced a per-user thermal model, the response uses the comfort band derived from your preferences with setpoint_temps: null:
{ "date": "2025-11-18", "schedule": { "intervals": [0, 1, "...", 47], "high_temps": [73.5, 73.5, "...(all same)"], "low_temps": [70.5, 70.5, "...(all same)"] }, "mode": "cool", "estimated_savings_pct": 0, "model_confidence": null, "generated_at": "2025-11-18T12:00:00+00:00", "source": "defaults"}Integration pattern
Section titled “Integration pattern”- Call
GET /api/v1/scheduleonce when your client starts and once daily (e.g., after 06:30 UTC when nightly + initial-fit have both completed). - Cache the response locally.
- Every 30 minutes, look up the current interval
idxand applysetpoint_temps[idx]to your thermostat. Ifsetpoint_tempsisnull, fall back to the midpoint ofhigh_temps[idx]andlow_temps[idx].
Errors
Section titled “Errors”| Status | Detail | Cause |
|---|---|---|
| 401 | "Not authenticated" | Missing or invalid token |
POST /api/v1/schedule/recompute
Section titled “POST /api/v1/schedule/recompute”Run optimization synchronously for the authenticated user and return the freshly-written schedules. Designed for “user just hit Save on a constraint editor — show them the new chart now” flows; the response is the same shape as GET /api/v1/schedules (all appliances at once), not a single schedule.
Auth: Required (Bearer JWT)
Example
Section titled “Example”curl -X POST https://api.hungrymachines.io/api/v1/schedule/recompute \ -H "Authorization: Bearer YOUR_TOKEN"Response (200)
Section titled “Response (200)”Same shape as GET /api/v1/schedules:
{ "date": "2025-11-18", "appliances": [ { "appliance_id": "...", "appliance_type": "hvac", "name": "Living Room AC", "schedule": { "intervals": [0, 1, "...", 47], "high_temps": [74.0, "..."], "low_temps": [70.0, "..."], "setpoint_temps": [72.0, "..."], "temp_trajectory": [72.1, "..."] }, "savings_pct": 18.5, "source": "optimization", "entities": { "entity_id": "climate.thermostat" } } ]}Latency
Section titled “Latency”Typically 1–5 seconds. Worst case is bounded by the HVAC optimizer’s 28-second budget plus a couple of fast non-HVAC runs. Show an “Optimizing…” overlay while the request is in flight.
When to use it
Section titled “When to use it”- Right after the user changes constraints, comfort bands, or
optimization_modeand expects to see the impact immediately. - After saving a custom rate curve via
PUT /api/v1/rates.
For the regular daily fetch, use GET /api/v1/schedule or GET /api/v1/schedules — they read the most recent persisted row without triggering a recompute.
Errors
Section titled “Errors”| Status | Detail | Cause |
|---|---|---|
| 401 | "Not authenticated" | Missing or invalid token |
| 503 | "Database unavailable" | Supabase not reachable |