Webhooks
Bikeep can send HTTP webhook notifications to your server when device events occur. Webhooks are useful for monitoring device health, alerting on alarms and anomalous device states, tracking connectivity, and receiving reports.
Setup
Webhook endpoints are configured by Bikeep. To register your endpoint:
- Email info@bikeep.com with your endpoint URL
- Bikeep will configure the webhook and provide you with a seed for hash verification
- Specify which event types you want to receive (see table below)
Tip: You can request alarm filtering by category or reason — for example, receive only
securityalarms, or onlywire_cutevents. Specify your filter preferences during setup.
Webhook Types
| Type | Tier | When It Fires | Delivery | Key Fields |
|---|---|---|---|---|
connection |
Public | Device goes online or offline | Aggregated (5 min) | connection |
connection_confirmed |
Public | Location offline for 2+ hours — confirms a real outage, not a brief flap | Aggregated (15 min) | connection |
alarm |
Public | Hardware alarm triggered | Aggregated (1 min) | alarm_reason, alarm_category |
parking_reminder |
Public | Parking duration reminder | Aggregated (1 day) | parking_details[] |
status |
Partner | Anomalous device state persisted (e.g., door left open 5+ min, interim state timed out) | Aggregated (5 min) | status |
report |
Partner | Report completed | Direct | report_type, report_url, url_valid_until |
maintenance_log |
Partner | Maintenance log entry created, updated, or deleted | Direct | action, log_entry_id, description, targets |
Availability Tiers
- Public types are available to all integrators with API access.
- Partner types require partner-level access. Contact Bikeep if you need these event types.
Payload Format
All webhooks are sent as POST requests with a JSON body. The version 2 envelope includes a top-level type field identifying the event type:
{
"type": "connection",
"data": { ... },
"version": 2,
"hash": "4161215c299681565b4309c7c60dddce0b25b86d..."
}
| Field | Description |
|---|---|
type |
Event type identifier (e.g., "connection", "alarm", "status") |
data |
Event payload — structure varies by type (see examples below) |
version |
Payload format version (currently 2) |
hash |
SHA-256 hash for verifying payload authenticity (see Hash Verification) |
Connection payload
Sent when a device goes online or offline.
{
"type": "connection",
"data": {
"type": "connection_changed",
"location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"location_name": "#1234 Office Building A",
"location_address": "123 Main Street",
"location_code": "#1234",
"location_coordinates": {
"latitude": 59.42776,
"longitude": 24.81521
},
"location_status": "LAUNCHED",
"connection": "offline",
"timestamp": "2025-12-15T10:30:00Z",
"devices": [
{
"device_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"device_nr": "1"
}
]
},
"hash": "4161215c299681565b4309c7c60dddce0b25b86d36ce72a1e43b58d8f7be88cf",
"version": 2
}
Note: The
data.typefield contains"connection_changed"for backwards compatibility with version 1 consumers. The top-leveltypefield contains the canonical name"connection".
Connection Confirmed payload
Sent when a location has been offline for 2+ hours, confirming a real outage rather than a brief connectivity flap. This webhook fires only for offline events — it does not notify when a location comes back online. Use the connection webhook or poll the location endpoint for online-recovery detection.
{
"type": "connection_confirmed",
"data": {
"location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"location_name": "#1234 Office Building A",
"location_address": "123 Main Street",
"location_code": "#1234",
"location_coordinates": {
"latitude": 59.42776,
"longitude": 24.81521
},
"location_status": "LAUNCHED",
"connection": "offline",
"timestamp": "2025-12-15T12:30:00Z",
"devices": [
{
"device_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"device_nr": "1"
}
]
},
"hash": "b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890ab",
"version": 2
}
Alarm payload
Sent when a hardware alarm is triggered (e.g., case open, wire cut, low voltage).
{
"type": "alarm",
"data": {
"type": "alarm",
"location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"location_name": "#1234 Office Building A",
"location_address": "123 Main Street",
"location_code": "#1234",
"location_coordinates": {
"latitude": 59.42776,
"longitude": 24.81521
},
"location_status": "LAUNCHED",
"alarm_reason": "case_open",
"alarm_category": "security",
"timestamp": "2025-12-15T10:35:00Z",
"devices": [
{
"device_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"device_nr": "1"
}
]
},
"hash": "a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890",
"version": 2
}
Note: The
data.typefield contains"alarm"for backwards compatibility with version 1 consumers.
Alarm categories and reasons
| Category | Reasons |
|---|---|
security |
wire_cut, case_open, lock_physically_opened, tampered |
technical |
ups_power_lost, ground_fault, low_voltage, not_responding, camera_not_responding |
Parking Reminder payload
Sent as a daily reminder listing devices with active parking sessions exceeding a configured duration threshold.
{
"type": "parking_reminder",
"data": {
"timestamp": "2025-12-16T08:00:00Z",
"location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"location_name": "#1234 Office Building A",
"location_address": "123 Main Street",
"location_code": "#1234",
"location_coordinates": {
"latitude": 59.42776,
"longitude": 24.81521
},
"location_status": "LAUNCHED",
"parking_details": [
{
"user_id": "c3d4e5f6-a1b2-7890-abcd-ef1234567890",
"device_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"device_alias": "#1234-01",
"duration": 172800
},
{
"user_id": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"device_id": "a9b8c7d6-e5f4-3210-abcd-ef1234567890",
"device_alias": "#1234-02",
"duration": 86400
}
]
},
"hash": "c3d4e5f6a1b27890abcdef1234567890abcdef1234567890abcdef1234567890",
"version": 2
}
| Field | Description |
|---|---|
parking_details[].user_id |
ID of the user with an active parking session |
parking_details[].device_id |
Device where the item is parked |
parking_details[].device_alias |
Human-readable device identifier |
parking_details[].duration |
Parking duration in seconds |
Status payload
Sent when a device is in an anomalous state that has persisted past its expected duration — for example, a door (bike house door or locker door) left open for more than 5 minutes, or an interim state (ALLOCATED, LOCKING, UNLOCKING) that did not resolve within its timeout. This is an alarming webhook for detecting stuck or abnormal devices, not a general event stream for device state changes. Requires partner-level access.
{
"type": "status",
"data": {
"location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"location_name": "#1234 Office Building A",
"location_address": "123 Main Street",
"location_code": "#1234",
"location_coordinates": {
"latitude": 59.42776,
"longitude": 24.81521
},
"location_status": "LAUNCHED",
"status": "UNLOCKING",
"timestamp": "2025-12-15T10:32:00Z",
"devices": [
{
"device_id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890",
"device_nr": "1"
}
]
},
"hash": "d4e5f6a1b2c37890abcdef1234567890abcdef1234567890abcdef1234567890",
"version": 2
}
The status field contains the device’s current (anomalous) state. In practice you will see interim states that failed to resolve: ALLOCATED, LOCKING, UNLOCKING. See State Machines for what each state means and its expected duration.
Note: The
location_statusfield in the webhook payload (e.g.,LAUNCHED,MAINTENANCE) describes the location’s operational status, not the device state. Do not confuselocation_statuswith thestatusfield.
Maintenance Log payload
Sent when a maintenance log entry is created, updated, or deleted. Requires partner-level access.
{
"type": "maintenance_log",
"data": {
"timestamp": "2025-12-15T10:00:00Z",
"organization_id": "e5f6a1b2-c3d4-7890-abcd-ef1234567890",
"location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"location_name": "#1234 Office Building A",
"location_code": "#1234",
"location_status": "LAUNCHED",
"action": "created",
"log_entry_id": "d4e5f6a1-b2c3-7890-abcd-ef1234567890",
"description": "Replaced locking mechanism on device #3",
"targets": ["f1e2d3c4-b5a6-7890-abcd-ef1234567890"],
"priority": "high",
"status": "open",
"status_changed_at": "2025-12-15T10:00:00Z",
"seconds_spent": 3600,
"resolution": "Replaced faulty component",
"cost": "150.00"
},
"hash": "f6a1b2c3d4e57890abcdef1234567890abcdef1234567890abcdef1234567890",
"version": 2
}
| Field | Description |
|---|---|
location_id |
UUID of the associated location |
location_name |
Human-readable location name |
location_code |
Short location code (e.g., #1234) |
location_status |
Location operational status (e.g., LAUNCHED, MAINTENANCE) |
action |
The action performed: created, updated, or deleted |
log_entry_id |
UUID of the maintenance log entry |
description |
Human-readable description of the maintenance task |
targets |
List of affected device or location UUIDs |
priority |
Priority level of the maintenance entry |
status |
Maintenance entry status (e.g., open, resolved) |
seconds_spent |
Time spent on the maintenance task in seconds |
resolution |
Resolution description (if resolved) |
cost |
Cost of the maintenance task |
Report payload
Sent when a report has been generated and is ready for download. Requires partner-level access. This event has a different structure — it contains organization and report metadata instead of location fields.
{
"type": "report",
"data": {
"timestamp": "2025-12-15T11:00:00Z",
"organization_id": "e5f6a1b2-c3d4-7890-abcd-ef1234567890",
"report_id": "rpt-1234-5678-abcd",
"report_type": "usage_summary",
"report_description": "Monthly usage summary for December 2025",
"report_url": "https://reports.bikeep.com/download/rpt-1234-5678-abcd",
"url_valid_until": "2025-12-22T11:00:00Z"
},
"hash": "e5f6a1b2c3d47890abcdef1234567890abcdef1234567890abcdef1234567890",
"version": 2
}
| Field | Description |
|---|---|
report_url |
Temporary download URL for the report file |
url_valid_until |
Expiration time of the download URL (UTC, ISO 8601) |
Versioning
The current webhook payload version is 2. Changes from version 1:
- A top-level
typefield was added to the envelope, identifying the event type (e.g.,"connection","alarm","status") - New event types were added:
connection_confirmed,parking_reminder,status,report - New fields were added to existing events:
location_status,alarm_category
For backwards compatibility, the connection and alarm events retain a data.type field with their version 1 names ("connection_changed" and "alarm" respectively). Newer event types do not include data.type.
The hash verification algorithm is unchanged between versions — it still uses SHA256(JSON(data) + seed).
Hash Verification
Every webhook includes a hash field for verifying the payload’s authenticity. The hash is computed as:
SHA256( JSON(data) + seed )
Where:
JSON(data)is the JSON-serializeddataobject from the payloadseedis the pre-shared secret provided by Bikeep during webhook setup- The
+is string concatenation
Verification example (Node.js)
const crypto = require('crypto');
function verifyWebhook(payload, seed) {
const dataJson = JSON.stringify(payload.data);
const expectedHash = crypto
.createHash('sha256')
.update(dataJson + seed)
.digest('hex');
return expectedHash === payload.hash;
}
Verification example (Python)
import hashlib
import json
def verify_webhook(payload, seed):
data_json = json.dumps(payload['data'], separators=(',', ':'))
expected = hashlib.sha256((data_json + seed).encode()).hexdigest()
return expected == payload['hash']
Response Requirements
Your endpoint must:
- Respond with a 200 status code to acknowledge receipt
- Respond within 5 seconds (requests time out after 5 seconds per attempt)
Non-200 responses with status code 5xx trigger retries (see below). Client errors (4xx) are not retried.
Delivery Characteristics
Delivery modes
| Mode | Behavior | Availability |
|---|---|---|
| Aggregated | Events are batched and delivered at regular intervals to prevent flooding | connection, connection_confirmed, alarm, parking_reminder, status |
| Direct | Events are delivered immediately as they occur | report, maintenance_log |
Aggregation intervals
| Event Type | Default Interval |
|---|---|
alarm |
1 minute |
connection |
5 minutes |
connection_confirmed |
15 minutes |
status |
5 minutes |
parking_reminder |
1 day |
report |
Immediate (always direct) |
maintenance_log |
Immediate (always direct) |
Why aggregation? IoT devices can fire many events rapidly (e.g., connectivity flapping, rapid state changes). Aggregation batches these into regular intervals to avoid overwhelming your endpoint.
Retry policy
- Retries: Up to 3 retries on 5xx server errors and network/connection errors
- Backoff: 100 ms between retries
- Not retried: 4xx client errors
- Ordering: Not guaranteed — use the
timestampfield for ordering