Start forecasting
Use your historic attendance data to generate AI-driven forecasts for people, teams, and locations, helping predict future space needs.
Prerequisites
Completed Add historic attendance (including session finish if you batched)
Node.js v18+ and npm v9+
A valid gospace API key in your
.env
fileAt least one populated location with historic occupancy data
1) Trigger a forecast run
If your project is configured for automatic rebuilds, you can skip this step. Otherwise, trigger forecasting explicitly.
Create start-forecast.ts
:
import "dotenv/config";
import GospaceAI from "@gospace-ai/api";
async function main() {
const gospace = new GospaceAI(process.env.GOSPACE_API_KEY!);
// Kick off a forecast rebuild for a location and optional date range
const res = await gospace.system.startForecast({
location_id: "68499cf4af8729934aae208a",
// Optional time window. If omitted, backend will choose sensible defaults.
// starts_at: "2025-06-20T00:00:00Z",
// ends_at: "2025-07-31T23:59:59Z",
// Optional control flags
// force_rebuild: true
});
console.log(JSON.stringify(res.data, null, 2));
// Expect a job or task identifier you can poll
}
main().catch((err) => {
console.error("Start forecast failed:", err);
process.exit(1);
});
2) (Optional) Poll job status
If startForecast
returns a job, poll until it’s complete.
import "dotenv/config";
import GospaceAI from "@gospace-ai/api";
const JOB_ID = "your_forecast_job_id";
async function main() {
const gospace = new GospaceAI(process.env.GOSPACE_API_KEY!);
for (;;) {
const status = await gospace.system.getForecastJob({ job_id: JOB_ID });
console.log(status.data.state);
if (status.data.state === "completed") break;
if (status.data.state === "failed") throw new Error("Forecast job failed");
await new Promise((r) => setTimeout(r, 3000));
}
console.log("Forecast ready.");
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
3) Fetch forecasts
A) People forecast (per person, optionally scoped to team)
Create get-people-forecast.ts
:
import "dotenv/config";
import GospaceAI from "@gospace-ai/api";
async function main() {
const gospace = new GospaceAI(process.env.GOSPACE_API_KEY!);
const res = await gospace.system.getPeopleForecast({
location_id: "68499cf4af8729934aae208a",
// Optional filters:
// people_id: "68518a559c86c18f01ee84e4",
// team_id: "68518a549c86c18f01ee84b0",
starts_at: "2025-06-29T23:00:00Z",
ends_at: "2025-07-07T22:59:00Z",
skip: 0,
limit: 25,
});
// Example shape (truncated):
// {
// success: true,
// data: { forecast: [ { starts_at, ends_at, location_id, people_id, team_id, forecasted_desk, forecasted_room, forecasted_total }, ... ] },
// pagination: { current_results: { from, to }, total_results },
// identifier: "get_forecast_successful"
// }
console.log(JSON.stringify(res.data, null, 2));
}
main().catch((err) => {
console.error("Get people forecast failed:", err);
process.exit(1);
});
B) Team forecast (aggregated by team)
Create get-team-forecast.ts
:
import "dotenv/config";
import GospaceAI from "@gospace-ai/api";
async function main() {
const gospace = new GospaceAI(process.env.GOSPACE_API_KEY!);
const res = await gospace.system.getTeamForecast({
location_id: "68499cf4af8729934aae208a",
starts_at: "2025-06-20T23:00:00Z",
ends_at: "2025-06-22T22:59:00Z",
skip: 0,
limit: 25,
});
// Records include *_inc fields:
// forecasted_desks, forecasted_rooms, forecasted_total,
// forecasted_desks_inc, forecasted_rooms_inc, forecasted_total_inc
console.log(JSON.stringify(res.data, null, 2));
}
main().catch((err) => {
console.error("Get team forecast failed:", err);
process.exit(1);
});
C) Location forecast (location‑level totals)
Create get-location-forecast.ts
:
import "dotenv/config";
import GospaceAI from "@gospace-ai/api";
async function main() {
const gospace = new GospaceAI(process.env.GOSPACE_API_KEY!);
const res = await gospace.system.getLocationForecast({
location_id: "68499cf4af8729934aae208a",
starts_at: "2025-06-20T23:00:00Z",
ends_at: "2025-07-23T22:59:00Z",
skip: 0,
limit: 25,
});
// Records include *_inc fields for adjusted capacity planning.
console.log(JSON.stringify(res.data, null, 2));
}
main().catch((err) => {
console.error("Get location forecast failed:", err);
process.exit(1);
});
4) Turn forecasts into capacity requirements
Daily desk and room requirements (using *_inc)
type LocationForecast = {
starts_at: string;
ends_at: string;
location_id: string;
forecasted_desks_inc: number;
forecasted_rooms_inc: number;
forecasted_total_inc: number;
};
function summarizeCapacity(rows: LocationForecast[]) {
return rows.reduce(
(acc, r) => {
acc.maxDesks = Math.max(acc.maxDesks, r.forecasted_desks_inc);
acc.maxRooms = Math.max(acc.maxRooms, r.forecasted_rooms_inc);
acc.maxTotal = Math.max(acc.maxTotal, r.forecasted_total_inc);
return acc;
},
{ maxDesks: 0, maxRooms: 0, maxTotal: 0 }
);
}
// Example usage:
// const summary = summarizeCapacity(res.data.forecast);
// console.log(summary); // { maxDesks, maxRooms, maxTotal }
Aggregating per‑team to size team neighborhoods
type TeamForecast = {
team_id: string;
starts_at: string;
forecasted_desks_inc: number;
};
function peakPerTeam(rows: TeamForecast[]) {
const map = new Map<string, number>();
for (const r of rows) {
map.set(r.team_id, Math.max(map.get(r.team_id) ?? 0, r.forecasted_desks_inc));
}
return map; // team_id -> peak desks required
}
Checking person‑level confidence
Use people forecasts to validate outliers (e.g., unexpected spikes for specific individuals or teams) before making seating or room allocation changes.
Tips
Use
_inc
fields to plan for the real world (targets, bookings, contingencies).Choose consistent day boundaries (
starts_at
/ends_at
) that match your business day/timezone.Use pagination (
skip
,limit
) to iterate long horizons.Rebuild forecasts after significant new history or policy changes (e.g., new minimum targets).
Last updated
Was this helpful?