HTTP Streaming

Your application can use the HTTP pull channel to retrieve events over HTTP/2 or HTTP/1.x. Initiate a GET request to the Firehose HTTP endpoint, and events are continuously sent as newline-delimited JSON as long as the connection is active.

The OpenAPI specification for this endpoint, including the full EventRecord schema and every event payload, is available as firehose-streaming.openapi.json.

Endpoint

GET /api/partners/v1/firehose/events

Parameters

Parameter Type Required Default Description
minPartition integer No 1 Start of partition range (1–12).
maxPartition integer No 12 End of partition range (1–12).
fromTimestamp integer No 0 Epoch milliseconds to resume consuming from. 0 = latest only.
replicaId integer No 0 On-prem replica identifier. Not applicable for cloud activations.

Example

curl -N -H 'X-API-KEY: <api-key>' \
  'https://partners.ciscospaces.io/api/partners/v1/firehose/events'

Response Format

The response is a continuous stream of newline-delimited JSON (NDJSON) using HTTP chunked transfer encoding with Connection: keep-alive.

  • Each line is a complete JSON object representing one event, terminated by \n.
  • Your application must support secure (HTTPS) connections.
  • HTTP/2 is supported and recommended for lower connection overhead.

Processing the Stream

  1. Read data from the response body
  2. Split on \n
  3. Parse each complete line as JSON
  4. Process the event
  5. Repeat

Event Record Structure

Every event contains these common fields plus one event-specific payload field:

Field Type Description
recordUid string Unique event identifier (use for deduplication)
recordTimestamp integer Epoch milliseconds when the event was generated
spacesTenantId string Cisco Spaces tenant identifier
spacesTenantName string Cisco Spaces tenant display name
partnerTenantId string Partner tenant identifier
eventType string Event category (see Event Types)
(event-specific) object Payload keyed by camelCase event name (e.g., deviceLocationUpdate)

JSON field names use camelCase (e.g., recordUid, deviceLocationUpdate). The canonical schema is defined in Protocol Buffers using snake_case — JSON names are the camelCase equivalent (e.g., record_uidrecordUid).

New fields may be added without notice. Use a JSON parser that ignores unknown properties.

For a comprehensive reference with every event type and all fields populated, see Sample Events.

Error Handling

Status Cause
401 API key missing, invalid, or blocked
403 HTTP endpoint type not enabled for activation
400 Invalid replicaId for cloud activation
429 Rate limit exceeded; honour Retry-After header
500 Internal server error

See Connection Management for rate limiting details and reconnection strategy.

Example Payloads

Device Location Update

{
  "recordUid": "event-a1b2c3d4",
  "recordTimestamp": 1714989902000,
  "spacesTenantId": "spaces-tenant-0db42b45",
  "spacesTenantName": "Sample Tenant",
  "partnerTenantId": "partner-tenant-12345",
  "eventType": "DEVICE_LOCATION_UPDATE",
  "deviceLocationUpdate": {
    "device": {
      "deviceId": "device-kqtBjbYJS619EoqIVRBSf",
      "macAddress": "00:11:22:33:44:55"
    },
    "location": {
      "locationId": "location-d827508f",
      "name": "Level 3",
      "inferredLocationTypes": ["FLOOR"]
    },
    "ssid": "Corporate-WiFi",
    "lastSeen": 1714989900000,
    "mapId": "26752a276ac412620baf2822def1f523",
    "xPos": 12.4,
    "yPos": 19.8,
    "confidenceFactor": 0.85,
    "latitude": 51.505,
    "longitude": -0.023
  }
}

Device Counts

{
  "recordUid": "event-e5f6g7h8",
  "recordTimestamp": 1714989960000,
  "spacesTenantId": "spaces-tenant-0db42b45",
  "spacesTenantName": "Sample Tenant",
  "partnerTenantId": "partner-tenant-12345",
  "eventType": "DEVICE_COUNT",
  "deviceCounts": {
    "location": {
      "locationId": "location-d827508f",
      "name": "Level 3"
    },
    "associatedCount": 142,
    "estimatedProbingCount": 87,
    "probingRandomizedPercentage": 0.65,
    "associatedDelta": 3,
    "probingDelta": -2
  }
}

IoT Telemetry

{
  "recordUid": "event-i9j0k1l2",
  "recordTimestamp": 1714989905000,
  "spacesTenantId": "spaces-tenant-0db42b45",
  "spacesTenantName": "Sample Tenant",
  "partnerTenantId": "partner-tenant-12345",
  "eventType": "IOT_TELEMETRY",
  "iotTelemetry": {
    "deviceInfo": {
      "deviceId": "ble-sensor-001",
      "deviceMacAddress": "AA:BB:CC:DD:EE:FF",
      "deviceType": "IOT_BLE_DEVICE"
    },
    "location": {
      "locationId": "location-d827508f",
      "name": "Level 3"
    },
    "temperature": {
      "temperatureInCelsius": 22.5
    },
    "battery": {
      "value": 85,
      "unit": "PERCENTAGE"
    }
  }
}

Keep-Alive

{"recordUid":"event-abc123","recordTimestamp":1714989902000,"eventType":"KEEP_ALIVE"}

Client Examples

Python

import requests, json

url = 'https://partners.ciscospaces.io/api/partners/v1/firehose/events'
headers = {'X-API-KEY': '<api-key>'}

with requests.get(url, headers=headers, stream=True) as response:
    for line in response.iter_lines():
        if line:
            event = json.loads(line)
            if event.get('eventType') == 'KEEP_ALIVE':
                continue
            print(f"{event['eventType']}: {event['recordUid']}")

Java/Kotlin

val connection = URL(url).openConnection() as HttpURLConnection
connection.setRequestProperty("X-API-KEY", apiKey)

BufferedReader(InputStreamReader(connection.inputStream)).use { reader ->
    var line: String?
    while (reader.readLine().also { line = it } != null) {
        val event = JsonParser.parseString(line).asJsonObject
        val eventType = event.get("eventType").asString
        if (eventType == "KEEP_ALIVE") continue
        // Process event
    }
}

Node.js

const https = require('https');

const options = {
  hostname: 'partners.ciscospaces.io',
  path: '/api/partners/v1/firehose/events',
  headers: { 'X-API-KEY': '<api-key>' }
};

https.get(options, (res) => {
  let buffer = '';
  res.on('data', (chunk) => {
    buffer += chunk;
    const lines = buffer.split('\n');
    buffer = lines.pop();
    for (const line of lines) {
      if (line) {
        const event = JSON.parse(line);
        if (event.eventType === 'KEEP_ALIVE') continue;
        console.log(`${event.eventType}: ${event.recordUid}`);
      }
    }
  });
});

See Also