# 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developer.gospace.com/start-building/add-historic-attendance.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
