Integrate with Hubble Network Platform API for device management, packet retrieval with streaming, webhook configuration, metrics tracking, user management, and billing. Use when working with Hubble IoT devices, retrieving packet data, setting up webhooks, or managing deployments on Hubble Network.
This skill inherits all available tools. When active, it can use any tool Claude has access to.
API_REFERENCE.mdEXAMPLES.mdTROUBLESHOOTING.mdWORKFLOWS.mdresources/hubble-openapi.yamlHubble Network connects off-the-shelf Bluetooth chips to a global network of 90M+ gateways. This skill helps you integrate with the Hubble Cloud API to manage devices, retrieve packet data, configure webhooks, monitor metrics, and manage your organization.
API Base URL: https://api.hubble.com
API Version: Rolling release with continuous deployment and backward compatibility
Device: A Bluetooth-capable hardware endpoint registered on Hubble Network. Each device has:
hubnet.platform: LoRaWAN)Packet: Base64-encoded Bluetooth data transmitted by devices and received via gateways. Packets contain:
Webhook: HTTP endpoints you control that receive real-time packet data in batches. Key features:
Organization: Top-level entity containing devices, API keys, users, and webhooks. Organizations have:
API Keys: JWT bearer tokens with 16 distinct authorization scopes controlling access to:
All API requests require a JWT bearer token in the Authorization header:
Authorization: Bearer <your-jwt-token>
Important: API keys cannot be retrieved after creation. Store them securely.
The API supports 16 distinct scopes for granular access control:
Device Management:
devices:read - List and retrieve device informationdevices:write - Register, update, and delete devicesPacket Access:
packets:read - Query and stream packet dataWebhook Management:
webhooks:read - List webhook configurationswebhooks:write - Create, update, and delete webhooksOrganization Management:
organization:read - View organization detailsorganization:write - Update organization settingsUser Management:
users:read - List organization usersusers:write - Invite, update, and remove usersInvitations:
invitations:read - List pending invitationsinvitations:write - Send and revoke invitationsAPI Keys:
api_keys:read - List API keysapi_keys:write - Create and delete API keysMetrics:
metrics:read - Access API, packet, webhook, and device metricsBilling:
billing:read - View invoices and usage dataBest Practice: Request only the scopes your application needs. For read-only integrations, use *:read scopes only.
Response headers indicate your current rate limit status:
X-RateLimit-Limit: 3
X-RateLimit-Remaining: 2
X-RateLimit-Reset: 1640000000
When you receive a 429 response:
Retry-After header (seconds to wait)Example exponential backoff:
import time
def make_request_with_backoff(func, max_retries=5):
for i in range(max_retries):
response = func()
if response.status_code != 429:
return response
wait_time = 2 ** i # Exponential: 1, 2, 4, 8, 16 seconds
time.sleep(wait_time)
raise Exception("Max retries exceeded")
The Hubble Cloud API provides 40+ endpoints organized into 9 categories. For complete endpoint documentation, see API_REFERENCE.md.
POST /api/v2/org/{org_id}/devices - Register new devices with encryption keysGET /api/org/{org_id}/devices - List devices with filtering and sortingGET /api/org/{org_id}/devices/{device_id} - Retrieve specific device detailsPATCH /api/org/{org_id}/devices/{device_id} - Update device metadata and tagsPATCH /api/org/{org_id}/devices - Batch update up to 1,000 devicesKey features: Device names, custom tags, platform tags, timestamp filtering
GET /api/org/{org_id}/packets - Stream packets with paginationParameters:
start_time / end_time - Time range (default 7 days)device_id - Filter by specific deviceplatform_tag - Filter by tag (e.g., hubnet.platform=LoRaWAN)Pagination: Uses Continuation-Token header for streaming large datasets
The API returns packets with a nested structure. Understanding this structure is critical for extracting device information and metadata:
{
"location": {
"timestamp": 1765598212.181298, // Unix timestamp (seconds)
"latitude": 47.61421,
"longitude": -122.31929,
"altitude": 829,
"horizontal_accuracy": 29,
"vertical_accuracy": 29
},
"device": {
"id": "bc17a947-7a4f-4cff-9127-340cc4005272", // Device UUID
"name": "Device Display Name",
"tags": ["tag1", "tag2"],
"payload": "SGVsbG8gV29ybGQ=", // Base64-encoded
"timestamp": "2025-01-15T10:30:45Z",
"rssi": -85, // Signal strength (dBm)
"sequence_number": 42,
"counter": 123
},
"network_type": "bluetooth"
}
Important Field Locations:
packet.device.id (UUID format)packet.device.namepacket.device.rssi (not at top level)packet.device.sequence_numberpacket.location.latitude and packet.location.longitudepacket.location.timestamp (Unix epoch in seconds)Common Mistakes:
packet.device_id - Does not existpacket.dev_eui - Does not exist at top levelpacket.rssi - Does not exist at top levelpacket.device.id - Correct way to get device IDpacket.device.rssi - Correct way to get signal strengthHelper Function Example:
function extractPacketInfo(packet) {
return {
deviceId: packet.device.id,
deviceName: packet.device.name,
rssi: packet.device.rssi,
sequence: packet.device.sequence_number,
location: {
lat: packet.location?.latitude,
lng: packet.location?.longitude,
timestamp: packet.location?.timestamp ?
new Date(packet.location.timestamp * 1000) : null
},
payload: packet.device.payload
};
}
POST /api/org/{org_id}/webhooks - Register webhook endpointGET /api/org/{org_id}/webhooks - List all webhooksPATCH /api/org/{org_id}/webhooks/{webhook_id} - Update webhook configurationDELETE /api/org/{org_id}/webhooks/{webhook_id} - Remove webhookConfiguration: URL, name, custom max batch size (10-1000 packets)
GET /api/org/{org_id}/api_metrics - API request metrics with hourly breakdownsGET /api/org/{org_id}/packet_metrics - Packet volume metricsGET /api/org/{org_id}/webhook_metrics - Webhook delivery success/failure ratesGET /api/org/{org_id}/device_metrics - Active and registered device countsTime ranges: Configurable days back (1-365) and intervals (hour/day/month)
GET /api/org/{org_id} - Retrieve organization detailsPATCH /api/org/{org_id} - Update name, address, contact info, timezoneGET /api/org/{org_id}/users - List users (paginated)POST /api/org/{org_id}/users - Add user directlyPATCH /api/org/{org_id}/users/{user_id} - Update role or nameDELETE /api/org/{org_id}/users/{user_id} - Remove userRoles: Admin, Member
GET /api/org/{org_id}/invitations - List pending invitationsPOST /api/org/{org_id}/invitations - Invite new userDELETE /api/org/{org_id}/invitations - Revoke invitationGET /api/org/{org_id}/check - Validate current API keyPOST /api/org/{org_id}/key - Provision new API key with scopesGET /api/org/{org_id}/key - List all API keysPATCH /api/org/{org_id}/key/{key_id} - Update key metadataDELETE /api/org/{org_id}/key/{key_id} - Revoke API keyGET /api/org/{org_id}/key_scopes - List available authorization scopesGET /api/org/{org_id}/billing/invoices - List recent invoicesGET /api/org/{org_id}/billing/invoices/{invoice_id}/pdf - Download PDFGET /api/org/{org_id}/billing/usage - Daily/monthly active device usageFor detailed step-by-step guides, see WORKFLOWS.md.
Device Onboarding:
/api/v2/org/{org_id}/devicesPacket Streaming:
/api/org/{org_id}/packets with time rangeContinuation-Token headerWebhook Setup:
/api/org/{org_id}/webhooks with endpoint URLHTTP-X-HUBBLE-TOKEN headerAPI Key Rotation:
/api/org/{org_id}/checkDevice Batch Update:
/api/org/{org_id}/devices with arrayFor complete, runnable examples, see EXAMPLES.md.
import requests
import base64
import os
def register_device(api_token, org_id, device_id, device_name):
# Generate device key (32 bytes)
device_key = os.urandom(32)
device_key_b64 = base64.b64encode(device_key).decode('utf-8')
url = f"https://api.hubble.com/api/v2/org/{org_id}/devices"
headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
payload = {
"name": device_name,
"dev_eui": device_id,
"device_key": device_key_b64
}
response = requests.post(url, json=payload, headers=headers)
return response.json() if response.status_code == 201 else response.text
curl -X GET "https://api.hubble.com/api/org/YOUR_ORG_ID/devices" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
For endpoints returning large datasets, the API uses continuation tokens instead of offset-based pagination:
Continuation-TokenContinuation-Token header setContinuation-Token is absentExample:
import requests
def stream_all_packets(api_token, org_id):
url = f"https://api.hubble.com/api/org/{org_id}/packets"
headers = {"Authorization": f"Bearer {api_token}"}
all_packets = []
continuation_token = None
while True:
if continuation_token:
headers["Continuation-Token"] = continuation_token
response = requests.get(url, headers=headers)
data = response.json()
all_packets.extend(data["packets"])
continuation_token = response.headers.get("Continuation-Token")
if not continuation_token:
break
return all_packets
Device Keys: Encryption keys are binary data and must be Base64-encoded for JSON transport:
import base64
# Binary key (32 bytes)
binary_key = b'\x1a\x2b\x3c...'
# Encode for API
encoded_key = base64.b64encode(binary_key).decode('utf-8')
# Decode from API response
decoded_key = base64.b64decode(encoded_key)
Packet Payloads: Similarly, packet payloads returned by the API are Base64-encoded:
# Decode packet payload
packet_payload_b64 = "SGVsbG8gV29ybGQ="
decoded_payload = base64.b64decode(packet_payload_b64)
Common Error: Forgetting to Base64-encode device keys results in 400 errors with message "Invalid deviceKey format".
For comprehensive troubleshooting, see TROUBLESHOOTING.md.
401 Unauthorized:
Authorization header403 Forbidden:
400 Bad Request (Device Registration):
429 Rate Limit Exceeded:
Retry-After header404 Not Found:
All responses include X-Request-ID header for debugging:
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
Include this ID when contacting support for faster issue resolution.
HTTP-X-HUBBLE-TOKEN header/api/org/{org_id}/api_metricsX-Request-ID in application logsX-RateLimit-* headersHubble sends POST requests to your webhook endpoint:
POST /your/webhook/endpoint HTTP/1.1
Host: your-domain.com
Content-Type: application/json
HTTP-X-HUBBLE-TOKEN: your-webhook-secret
{
"packets": [
{
"device_id": "device123",
"payload": "SGVsbG8gV29ybGQ=",
"timestamp": "2024-01-15T10:30:00Z",
"metadata": { ... }
}
]
}
Always validate the HTTP-X-HUBBLE-TOKEN header to prevent spoofing:
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = "your-webhook-secret"
@app.route('/webhook', methods=['POST'])
def handle_webhook():
# Validate token
token = request.headers.get('HTTP-X-HUBBLE-TOKEN')
if token != WEBHOOK_SECRET:
abort(403)
# Process packets
data = request.json
packets = data.get('packets', [])
for packet in packets:
process_packet(packet)
return '', 200
When encountering issues:
X-Request-ID in support requestsTest API Key:
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://api.hubble.com/api/org/YOUR_ORG_ID/check
Check Scopes:
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://api.hubble.com/api/org/YOUR_ORG_ID/key_scopes
The Hubble Cloud API provides comprehensive access to device management, packet streaming, webhook configuration, and organization administration. Key points to remember:
For detailed implementations, refer to: