# Add historic attendance

Send past attendance records to gospace to build a history of workplace usage, enabling forecasting and AI-based optimisation.

{% tabs %}
{% tab title="Node.js" %}

### Prerequisites

* Completed **Setup Your Development Environment** and **Send Your First API Request**
* Node.js v18+ and npm v9+
* A valid gospace API key in your `.env` file
* Existing **location\_id** or **location\_external\_id**
* Existing **people\_id** or **people\_external\_id**

{% hint style="info" %}
Required fields per record: one of `location_id` or `location_external_id`, one of `people_id` or `people_external_id`, and `seen_at`.\
Optional fields per record: `team_id`, `first_seen_at`, `last_seen_at`, `timezone`.\
If sending **multiple batches**, you must use a **session** (see “Batching with sessions”).
{% endhint %}

### 1) Send a single batch (no session)

Create `add-historic-attendance.ts`:

```ts
import "dotenv/config";
import GospaceAI from "@gospace-ai/api";

type AttendanceRecord = {
  location_id?: string;
  location_external_id?: string;
  people_id?: string;
  people_external_id?: string;
  seen_at: string;        // ISO 8601, e.g. "2025-08-11T14:30:00Z" (UTC unless timezone provided)
  team_id?: string;
  first_seen_at?: string; // ISO 8601
  last_seen_at?: string;  // ISO 8601
  timezone?: string;      // e.g., "Europe/London" (used to interpret seen_at/first/last when not UTC)
};

function toUtcIso(d: Date | string) {
  const dt = typeof d === "string" ? new Date(d) : d;
  return dt.toISOString().replace(/\.\d{3}Z$/, "Z");
}

async function main() {
  const gospace = new GospaceAI(process.env.GOSPACE_API_KEY!);

  const occupancy: AttendanceRecord[] = [
    {
      location_id: "loc_123",
      people_external_id: "ext-person-001",
      seen_at: toUtcIso("2025-08-10T09:15:00Z"),
      team_id: "team_789",
      first_seen_at: toUtcIso("2025-08-10T09:00:00Z"),
      last_seen_at: toUtcIso("2025-08-10T17:30:00Z"),
    },
    {
      location_external_id: "ext-loc-002",
      people_id: "person_def",
      seen_at: "2025-08-10T09:15:00",
      timezone: "Europe/London",
    },
  ];

  const res = await gospace.system.createOccupancy({ occupancy });
  console.log(JSON.stringify(res.data, null, 2));
}

main().catch((err) => {
  console.error("Add historic attendance failed:", err);
  process.exit(1);
});
```

***

### 2) Batching with sessions (required for multi-request imports)

When importing **large datasets in multiple requests**, wrap them in a **session**:

* **First batch** must include `session_started: true`. The API returns a `session_id`.
* **Intermediate batches** must include the returned `session_id`.
* **Final batch** must include both `session_id` and `session_finished: true`.

Create `add-historic-attendance-batched.ts`:

```ts
import "dotenv/config";
import GospaceAI from "@gospace-ai/api";

type AttendanceRecord = {
  location_id?: string;
  location_external_id?: string;
  people_id?: string;
  people_external_id?: string;
  seen_at: string;
  team_id?: string;
  first_seen_at?: string;
  last_seen_at?: string;
  timezone?: string;
};

function toUtcIso(d: Date | string) {
  const dt = typeof d === "string" ? new Date(d) : d;
  return dt.toISOString().replace(/\.\d{3}Z$/, "Z");
}

// Example generator of many records
function generateRecords(count: number): AttendanceRecord[] {
  const base = new Date("2025-08-10T09:00:00Z");
  return Array.from({ length: count }, (_, i) => ({
    location_id: "loc_123",
    people_id: `person_${i + 1}`,
    seen_at: toUtcIso(new Date(base.getTime() + i * 60_000)),
  }));
}

async function main() {
  const gospace = new GospaceAI(process.env.GOSPACE_API_KEY!);

  const all = generateRecords(2500);
  const chunkSize = 500;

  let sessionId: string | undefined;

  for (let i = 0; i < all.length; i += chunkSize) {
    const chunk = all.slice(i, i + chunkSize);

    const isFirst = i === 0;
    const isLast = i + chunkSize >= all.length;

    const payload: any = { occupancy: chunk };

    if (isFirst) payload.session_started = true;
    if (sessionId) payload.session_id = sessionId;
    if (isLast) {
      payload.session_finished = true;
      if (sessionId) payload.session_id = sessionId;
    }

    const res = await gospace.system.createOccupancy(payload);

    // Capture session_id from the first response
    if (isFirst) {
      sessionId = res.data?.session_id || res.data?.session?.session_id;
      if (!sessionId) {
        throw new Error("No session_id returned from first batch");
      }
    }

    console.log(
      `Batch ${i / chunkSize + 1} sent (${chunk.length} records)${
        isFirst ? " [session_started]" : ""
      }${isLast ? " [session_finished]" : ""}`
    );
  }

  console.log("All batches completed for session:", sessionId);
}

main().catch((err) => {
  console.error("Batched historic attendance failed:", err);
  process.exit(1);
});
```

**Notes for sessions**

* Use consistent IDs/external IDs across batches.
* If a session fails mid-way, you can retry a batch with the **same `session_id`**.
* Keep batch sizes reasonable (e.g., 500–1000 records) to avoid timeouts.
* Timestamps should be ISO‑8601. Use `timezone` if not sending UTC (`Z`).
  {% endtab %}
  {% endtabs %}
