| name | hubble-cloud-api |
| description | This skill should be used when the user needs to integrate with Hubble Network Platform API for IoT device management, packet data retrieval, webhook configuration, metrics tracking, user management, or billing. Triggers include 'Hubble API', 'Hubble devices', 'Hubble packets', 'register IoT devices', 'configure webhooks for Hubble', 'Hubble Network integration', 'stream packet data'. |
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