Examples

End-to-end working examples in cURL, TypeScript, and Python. Each builds on the previous — together they cover the full workflow: venue creation → hardware integration → event ingestion → occupancy query.

cURLTypeScriptPython
1

Create a venue

Register a physical location. The venue UUID returned here is used in all subsequent calls. Timezone must be a valid IANA string.

cURL
1-create-venue.sh
export GN_API_KEY="gn_live_sk_..."

curl -X POST https://api.guestnetworks.com/api/venues \
  -H "Authorization: Bearer $GN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name":     "Downtown Hotel",
    "timezone": "America/New_York",
    "address":  "123 Main St, New York, NY"
  }'

# Save the "id" from the response:
# export VENUE_ID="<uuid returned above>"
TypeScript
1-create-venue.ts
const GN_API_KEY = process.env.GN_API_KEY!; // gn_live_sk_...
const BASE = 'https://api.guestnetworks.com';

const headers = {
  'Authorization': `Bearer ${GN_API_KEY}`,
  'Content-Type': 'application/json',
};

const venueRes = await fetch(`${BASE}/api/venues`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    name:     'Downtown Hotel',
    timezone: 'America/New_York',
    address:  '123 Main St, New York, NY',
  }),
});

// The venue UUID is needed for all subsequent calls
const { id: venueId } = await venueRes.json();
console.log('Venue created:', venueId);
Python
1-create-venue.py
import os, requests

GN_API_KEY = os.environ["GN_API_KEY"]  # gn_live_sk_...
BASE = "https://api.guestnetworks.com"

headers = {
    "Authorization": f"Bearer {GN_API_KEY}",
    "Content-Type": "application/json",
}

venue = requests.post(
    f"{BASE}/api/venues",
    headers=headers,
    json={
        "name":     "Downtown Hotel",
        "timezone": "America/New_York",
        "address":  "123 Main St, New York, NY",
    },
)

venue_id = venue.json()["id"]
print("Venue created:", venue_id)
2

Connect a hardware integration

Attach a Meraki connector to the venue. The body is a discriminated union on type — swap "meraki" for "unifi", "aruba", "square", "density", or "verkada". Credentials are encrypted at rest.

cURL
2-connect-integration.sh
curl -X POST "https://api.guestnetworks.com/api/venues/$VENUE_ID/integrations" \
  -H "Authorization: Bearer $GN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type":    "meraki",
    "api_key": "'$MERAKI_API_KEY'",
    "org_id":  "553998"
  }'

# Response: { "id": "<integration-uuid>", "connector_type": "meraki", "is_active": true }
TypeScript
2-connect-integration.ts
const integrationRes = await fetch(
  `${BASE}/api/venues/${venueId}/integrations`,
  {
    method: 'POST',
    headers,
    body: JSON.stringify({
      type:    'meraki',
      api_key: process.env.MERAKI_API_KEY!,
      org_id:  '553998',
    }),
  },
);

const { id: integrationId } = await integrationRes.json();
console.log('Integration connected:', integrationId);
Python
2-connect-integration.py
integration = requests.post(
    f"{BASE}/api/venues/{venue_id}/integrations",
    headers=headers,
    json={
        "type":    "meraki",
        "api_key": os.environ["MERAKI_API_KEY"],
        "org_id":  "553998",
    },
)

integration_id = integration.json()["id"]
print("Integration connected:", integration_id)
3

Ingest an event

Push a canonical event to POST /ingest. connector_type matches the integration you just created. The payload is free-form — device_hash, zone_id, count, and metrics are read from it. Rate limit: 100 req/min.

cURL
3-ingest.sh
curl -X POST https://api.guestnetworks.com/ingest \
  -H "Authorization: Bearer $GN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "venue_id":       "'$VENUE_ID'",
    "connector_type": "meraki",
    "events": [
      {
        "type":      "device_connected",
        "timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%S.000Z)'",
        "payload": {
          "device_hash": "device_001",
          "metrics":     { "signal_strength": -42, "ssid": "Guest-WiFi" }
        }
      }
    ]
  }'

# → {"accepted":1,"rejected":0,"errors":[]}
TypeScript
3-ingest.ts
const ingestRes = await fetch(`${BASE}/ingest`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    venue_id:       venueId,
    connector_type: 'meraki',
    events: [
      {
        type:      'device_connected',
        timestamp: new Date().toISOString(),
        payload: {
          device_hash: 'device_001',
          metrics: { signal_strength: -42, ssid: 'Guest-WiFi' },
        },
      },
    ],
  }),
});

const result = await ingestRes.json();
console.log(result);
// → { accepted: 1, rejected: 0, errors: [] }
Python
3-ingest.py
from datetime import datetime, timezone

event = requests.post(
    f"{BASE}/ingest",
    headers=headers,
    json={
        "venue_id":       venue_id,
        "connector_type": "meraki",
        "events": [
            {
                "type":      "device_connected",
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "payload": {
                    "device_hash": "device_001",
                    "metrics":     {"signal_strength": -42, "ssid": "Guest-WiFi"},
                },
            },
        ],
    },
)

print(event.json())
# → {"accepted": 1, "rejected": 0, "errors": []}
4

Query occupancy via REST

Fetch time-series occupancy data for the last hour using the telemetry endpoint. TimescaleDB automatically selects the best rollup table (1-min for ≤60s intervals, 1-hr for ≤1hr, 1-day otherwise).

cURL
4-query-rest.sh
# Get the last 1 hour of occupancy in 5-minute buckets
NOW=$(date -u +%s%3N)
ONE_HOUR_AGO=$(( NOW - 3600000 ))

curl "https://api.guestnetworks.com/api/venues/$VENUE_ID/telemetry?keys=occupancy&startTs=$ONE_HOUR_AGO&endTs=$NOW&interval=300000&agg=AVG" \
  -H "Authorization: Bearer $GN_API_KEY"

# Response:
# {
#   "occupancy": [
#     { "ts": 1718447400000, "value": 42 },
#     { "ts": 1718447700000, "value": 58 },
#     ...
#   ]
# }
TypeScript
4-query-rest.ts
const endTs   = Date.now();
const startTs = endTs - 3_600_000; // 1 hour

const params = new URLSearchParams({
  keys:     'occupancy',
  startTs:  String(startTs),
  endTs:    String(endTs),
  interval: '300000',  // 5-minute buckets
  agg:      'AVG',
});

const telRes = await fetch(
  `${BASE}/api/venues/${venueId}/telemetry?${params}`,
  { headers },
);

const { occupancy } = await telRes.json();
// occupancy → [{ ts: number, value: number }, ...]
console.log('Latest occupancy:', occupancy.at(-1)?.value);
Python
4-query-rest.py
import time

end_ts   = int(time.time() * 1000)
start_ts = end_ts - 3_600_000  # 1 hour

params = {
    "keys":     "occupancy",
    "startTs":  start_ts,
    "endTs":    end_ts,
    "interval": 300000,   # 5-minute buckets
    "agg":      "AVG",
}

tel = requests.get(
    f"{BASE}/api/venues/{venue_id}/telemetry",
    headers=headers,
    params=params,
)

data = tel.json()
occupancy = data["occupancy"]
print("Latest occupancy:", occupancy[-1]["value"] if occupancy else "no data")
5

Query occupancy via MCP

The MCP endpoint at POST https://api.guestnetworks.com/mcp uses the same Authorization: Bearer header as the REST API. Connect with the MCP TypeScript SDK and call tools by name.

TypeScript (MCP)
5-query-mcp.ts
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from
  '@modelcontextprotocol/sdk/client/streamableHttp.js';

const client = new Client({ name: 'my-agent', version: '1.0' });

await client.connect(
  new StreamableHTTPClientTransport(
    new URL('https://api.guestnetworks.com/mcp'),
    {
      requestInit: {
        // Same Bearer token as REST — one key for everything
        headers: { 'Authorization': `Bearer ${process.env.GN_API_KEY}` },
      },
    },
  ),
);

const result = await client.callTool('get_occupancy', { venue_id: venueId });
const occupancy = JSON.parse(result.content[0].text);
console.log(occupancy);
// → { current: 47, capacity: 120, utilization: 0.392 }

await client.close();

Next steps