Hubble Cloud API Integration
Overview
Hubble 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
Domain Context
Key Concepts
Device: A Bluetooth-capable hardware endpoint registered on Hubble Network. Each device has:
- Unique device identifier (dev_eui)
- Device encryption keys for secure transmission
- Custom tags for organization and routing
- Platform-assigned tags (e.g.,
hubnet.platform: LoRaWAN)
Packet: Base64-encoded Bluetooth data transmitted by devices and received via gateways. Packets contain:
- Encrypted payload data
- Sequence numbers for ordering
- Authentication tags for verification
- Metadata (RSSI, SNR, gateway info, timestamps)
Webhook: HTTP endpoints you control that receive real-time packet data in batches. Key features:
- Push-based delivery (no polling required)
- Configurable batch sizes (10-1000 packets)
- Automatic retries on failure
- Delivery metrics for monitoring
Organization: Top-level entity containing devices, API keys, users, and webhooks. Organizations have:
- Unique organization ID (UUID)
- Multiple users with different roles
- API keys with granular scopes
- Billing and usage tracking
API Keys: JWT bearer tokens with 16 distinct authorization scopes controlling access to:
- Device management (read/write)
- Packet access (read)
- Webhook configuration (read/write)
- User management (read/write)
- Metrics and billing data (read)
Data Flow
- Device broadcasts Bluetooth packet
- Gateway receives and forwards to Hubble Network
- Platform processes and decrypts packet
- Data available via:
- Real-time webhooks (push notification)
- API streaming (pagination with continuation tokens)
- Metrics dashboard
Typical Use Cases
- Device onboarding: Register new IoT devices before deployment
- Packet retrieval: Stream historical or real-time packet data
- Webhook setup: Configure push notifications for packet delivery
- Fleet management: Tag, organize, and monitor large device deployments
- Metrics monitoring: Track API usage, packet volumes, webhook health
- Access control: Manage users and API key scopes
Authentication
Bearer Token Authentication
All API requests require a JWT bearer token in the Authorization header:
Authorization: Bearer <your-jwt-token>
Obtaining API Keys
- Log in to Hubble Dashboard
- Navigate to Developer → API Tokens
- Click Generate New Token
- Select required scopes (principle of least privilege)
- Copy and securely store the token
Important: API keys cannot be retrieved after creation. Store them securely.
Authorization Scopes
The API supports 16 distinct scopes for granular access control:
Device Management:
devices:read- List and retrieve device informationdevices:write- Register, update, and delete devices
Packet Access:
packets:read- Query and stream packet data
Webhook Management:
webhooks:read- List webhook configurationswebhooks:write- Create, update, and delete webhooks
Organization Management:
organization:read- View organization detailsorganization:write- Update organization settings
User Management:
users:read- List organization usersusers:write- Invite, update, and remove users
Invitations:
invitations:read- List pending invitationsinvitations:write- Send and revoke invitations
API Keys:
api_keys:read- List API keysapi_keys:write- Create and delete API keys
Metrics:
metrics:read- Access API, packet, webhook, and device metrics
Billing:
billing:read- View invoices and usage data
Best Practice: Request only the scopes your application needs. For read-only integrations, use *:read scopes only.
Rate Limits
Rate Limit Policy
- Per-endpoint limit: 3 requests/second
- Organization-wide limit: 15 requests/second
- HTTP 429: Rate limit exceeded
Rate Limit Headers
Response headers indicate your current rate limit status:
X-RateLimit-Limit: 3
X-RateLimit-Remaining: 2
X-RateLimit-Reset: 1640000000
Handling Rate Limits
When you receive a 429 response:
- Read
Retry-Afterheader (seconds to wait) - Implement exponential backoff (1s, 2s, 4s, 8s, ...)
- Use batch operations when available (e.g., batch device updates)
- Cache responses to reduce repeated requests
- Spread requests across time windows
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")
API Endpoints Overview
The Hubble Cloud API provides 40+ endpoints organized into 9 categories. For complete endpoint documentation, see API_REFERENCE.md.
Device Management
POST /api/v2/org/{org_id}/devices- Batch register devices (up to 1,000 per request)GET /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 devicesDELETE /api/org/{org_id}/devices- Batch delete up to 1,000 devices
Key features:
- Batch operations: Create, update, and delete up to 1,000 devices per API call
- Auto-generated credentials: API generates device IDs and encryption keys
- Encryption options: AES-256-CTR, AES-128-CTR, or NONE
- Device management: Names, custom tags, platform tags, timestamp filtering
Packet Retrieval
GET /api/org/{org_id}/packets- Stream packets with pagination
Parameters:
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
Packet Structure
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:
- Device ID:
packet.device.id(UUID format) - Device Name:
packet.device.name - RSSI:
packet.device.rssi(not at top level) - Sequence Number:
packet.device.sequence_number - GPS Location:
packet.location.latitudeandpacket.location.longitude - Timestamp:
packet.location.timestamp(Unix epoch in seconds)
Common Mistakes:
- ❌
packet.device_id- Does not exist - ❌
packet.dev_eui- Does not exist at top level - ❌
packet.rssi- Does not exist at top level - ✅
packet.device.id- Correct way to get device ID - ✅
packet.device.rssi- Correct way to get signal strength
Helper 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
};
}
Webhook Management
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 webhook
Configuration: URL, name, custom max batch size (10-1000 packets)
Metrics
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 counts
Time ranges: Configurable days back (1-365) and intervals (hour/day/month)
Organization Management
GET /api/org/{org_id}- Retrieve organization detailsPATCH /api/org/{org_id}- Update name, address, contact info, timezone
User Management
GET /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 user
Roles: Admin, Member
Invitation Management
GET /api/org/{org_id}/invitations- List pending invitationsPOST /api/org/{org_id}/invitations- Invite new userDELETE /api/org/{org_id}/invitations- Revoke invitation
API Key Management
GET /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 scopes
Billing
GET /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 usage
Common Workflows
For detailed step-by-step guides, see WORKFLOWS.md.
Quick Reference
Device Onboarding:
- Generate device credentials (device ID + encryption key)
- Base64-encode the encryption key
- POST to
/api/v2/org/{org_id}/devices - Verify registration with GET request
- Flash firmware to device with credentials
Packet Streaming:
- GET
/api/org/{org_id}/packetswith time range - Process returned packets
- Check for
Continuation-Tokenheader - If present, repeat request with token
- Continue until no more tokens
Webhook Setup:
- POST to
/api/org/{org_id}/webhookswith endpoint URL - Configure batch size (default 100, max 1000)
- Implement endpoint to receive POST requests
- Validate
HTTP-X-HUBBLE-TOKENheader - Return 200 status for successful receipt
- Monitor delivery with webhook metrics
API Key Rotation:
- POST new key with required scopes
- Update applications with new key
- Verify new key works with GET
/api/org/{org_id}/check - DELETE old key
Device Batch Update:
- GET devices to identify targets
- Prepare updates (max 1000 devices)
- PATCH
/api/org/{org_id}/deviceswith array - Verify changes with GET requests
Code Examples
For complete, runnable examples, see EXAMPLES.md.
Quick Python Example: Batch Register Devices
import requests
def batch_register_devices(api_token, org_id, n_devices=100, encryption="AES-128-CTR"):
"""
Register multiple devices in a single API call.
Args:
api_token: Hubble API bearer token
org_id: Organization UUID
n_devices: Number of devices to create (1-1000)
encryption: Encryption type (AES-256-CTR, AES-128-CTR, or NONE)
Returns:
List of device objects with generated credentials
"""
url = f"https://api.hubble.com/api/v2/org/{org_id}/devices"
headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
payload = {
"n_devices": n_devices,
"encryption": encryption
}
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 200:
data = response.json()
devices = data.get("devices", [])
print(f"Created {len(devices)} devices")
return devices
else:
print(f"Error: {response.status_code} - {response.text}")
return []
# Example usage
devices = batch_register_devices("your-api-token", "your-org-id", n_devices=10)
for device in devices:
print(f"Device ID: {device['device_id']}, Key: {device['key']}")
Quick Python Example: Batch Delete Devices
import requests
def batch_delete_devices(api_token, org_id, device_ids):
"""
Delete multiple devices in a single API call.
Args:
api_token: Hubble API bearer token
org_id: Organization UUID
device_ids: List of device UUIDs to delete (up to 1000)
Returns:
Dictionary with deletion results
"""
url = f"https://api.hubble.com/api/org/{org_id}/devices"
headers = {
"Authorization": f"Bearer {api_token}",
"Content-Type": "application/json"
}
payload = {"device_ids": device_ids}
response = requests.delete(url, json=payload, headers=headers)
if response.status_code in [200, 204]:
result = response.json() if response.text else {"deleted": len(device_ids)}
print(f"Deleted {result.get('deleted', len(device_ids))} devices")
return result
else:
print(f"Error: {response.status_code} - {response.text}")
return {"deleted": 0, "failed": len(device_ids)}
Quick curl Example: List Devices
curl -X GET "https://api.hubble.com/api/org/YOUR_ORG_ID/devices" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
Pagination
Continuation Token Pattern
For endpoints returning large datasets, the API uses continuation tokens instead of offset-based pagination:
- Make initial request to endpoint
- Process returned results (max 1000 items)
- Check response headers for
Continuation-Token - If present, make new request with
Continuation-Tokenheader set - Repeat until
Continuation-Tokenis absent
Example:
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
Data Encoding
Base64 Encoding Requirements
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".
Error Handling
For comprehensive troubleshooting, see TROUBLESHOOTING.md.
HTTP Status Codes
- 200 OK: Successful request
- 201 Created: Resource successfully created
- 400 Bad Request: Invalid request format or parameters
- 401 Unauthorized: Missing or invalid API token
- 403 Forbidden: Insufficient scopes for operation
- 404 Not Found: Resource does not exist
- 429 Too Many Requests: Rate limit exceeded
- 500 Internal Server Error: Server-side error (retry with backoff)
Common Error Patterns
401 Unauthorized:
- Missing
Authorizationheader - Expired or revoked API token
- Malformed token format
403 Forbidden:
- API key lacks required scope
- Attempting to access resources from different organization
400 Bad Request (Device Registration):
- Device key not Base64-encoded
- Missing required fields (name, dev_eui, device_key)
- Invalid device EUI format
429 Rate Limit Exceeded:
- Too many requests in time window
- Implement exponential backoff
- Check
Retry-Afterheader
404 Not Found:
- Invalid organization ID
- Device ID doesn't exist
- Webhook ID not found
Request Tracing
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.
Best Practices
Security
- Never commit API keys to version control
- Store tokens securely (environment variables, secret managers)
- Use minimal scopes - Only request permissions you need
- Rotate keys regularly - Implement key rotation workflow
- Validate webhook tokens - Check
HTTP-X-HUBBLE-TOKENheader
Performance
- Use batch operations - Update up to 1000 devices in single request
- Implement caching - Cache device lists, reduce repeated requests
- Stream with continuation tokens - Don't try to fetch all data at once
- Handle rate limits gracefully - Exponential backoff, not retry loops
- Use webhooks for real-time - Avoid polling for new packets
Data Handling
- Base64-encode binary data - Device keys and some payloads
- Validate data before API calls - Check formats locally first
- Handle pagination correctly - Use continuation tokens, not offsets
- Set appropriate time ranges - Default 7 days, adjust as needed
- Store device keys securely - Encryption keys are sensitive
Monitoring
- Track API metrics - Use
/api/org/{org_id}/api_metrics - Monitor webhook health - Check delivery success rates
- Set up alerting - For failed webhooks or API errors
- Log request IDs - Include
X-Request-IDin application logs - Track rate limit usage - Monitor
X-RateLimit-*headers
Error Recovery
- Implement retry logic - With exponential backoff
- Handle partial failures - In batch operations
- Log all errors - With context and request IDs
- Graceful degradation - Fall back to cached data
- Alert on repeated failures - Don't silently fail
Webhook Validation
Webhook Request Format
Hubble 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": { ... }
}
]
}
Validating Webhook Requests
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
Webhook Response
- Return 200 OK for successful receipt
- Hubble considers 2xx status codes as success
- Non-2xx responses trigger automatic retries
- Retries use exponential backoff
Getting Help
Resources
- API Documentation: https://docs.hubble.com/docs/api-specification/hubble-cloud-api
- OpenAPI Spec: See resources/hubble-openapi.yaml
- Developer Portal: https://hubble.com/developers
- Support Email: [email protected]
Debugging Checklist
When encountering issues:
- ✓ Check API key has required scopes
- ✓ Verify organization ID is correct
- ✓ Confirm Bearer token format in header
- ✓ Validate Base64 encoding for device keys
- ✓ Check rate limits aren't exceeded
- ✓ Review error response body for details
- ✓ Include
X-Request-IDin support requests
Quick Diagnostics
Test 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
Summary
The Hubble Cloud API provides comprehensive access to device management, packet streaming, webhook configuration, and organization administration. Key points to remember:
- Authentication: Bearer tokens with granular scopes
- Rate Limits: 3/sec per endpoint, 15/sec org-wide
- Pagination: Continuation tokens for streaming
- Encoding: Base64 for binary data (keys, payloads)
- Webhooks: Push notifications with validation
- Best Practices: Batch operations, caching, exponential backoff
For detailed implementations, refer to:
- API_REFERENCE.md - Complete endpoint documentation
- WORKFLOWS.md - Step-by-step implementation guides
- EXAMPLES.md - Runnable code examples
- TROUBLESHOOTING.md - Error resolution guides