The DIMO Vehicle Triggers API is being developed to deliver real-time vehicle notifications via webhooks. It aims to provide scalable and flexible event handling using Kafka.
Most actions are done via make commands. use make help to see all available commands.
make help
Specify a subcommand:
build build the binary
run run the binary
clean clean the binary
install install the binary
tidy tidy the go mod
test run tests
lint run linter
docker build docker image
tools-golangci-lint install golangci-lint
add-migration Generate migration file specify name with name=your_migration_name
generate run all file generation for the project
generate-swagger generate swagger documentation
generate-go run go generate
...To create a db:
$ docker compose up -d
Add a migrations:
$ make add-migration name=<migration_name>
Migrate DB to latest:
$ go run cmd/vehicle-triggers-api/main.go -migrate="only"
To regenerate models:
$ make generate-sqlboiler
- in your postgres:
create database vehicle_triggers_api with owner dimo;make sure you have userdimosetup in your local db. - check to see if kafka is installed locally
brew services list - install kafka with brew services
- list all topics:
/opt/homebrew/opt/kafka/bin/kafka-topics --list --bootstrap-server localhost:9092 - Create a topic:
/opt/homebrew/opt/kafka/bin/kafka-topics --create --topic topics.signals --bootstrap-server localhost:9092 --partitions 1 --replication-factor 1- Make sure to check .env to have the correct KAFKA_BROKERS and DEVICE_SIGNALS_TOPIC name
The Vehicle Triggers API provides webhook functionality for real-time vehicle telemetry notifications. Webhooks can be triggered by vehicle signals (like speed) or events (like harsh braking).
To register a webhook, make a POST request to /v1/webhooks with the following payload:
{
"service": "telemetry.signals",
"metricName": "speed",
"condition": "value > 55",
"coolDownPeriod": 30,
"description": "Alert when vehicle speed exceeds 55 mph",
"displayName": "Speed Alert",
"targetURL": "https://example.com/webhook",
"status": "enabled",
"verificationToken": "your-verification-token"
}service: The subsystem producing the metric. Must be either "telemetry.signals" or "telemetry.events"metricName: The fully qualified signal/event name to monitor (e.g., "speed", "HarshBraking")condition: A CEL expression that determines when the webhook firescoolDownPeriod: Minimum seconds between successive webhook callstargetURL: HTTPS endpoint that will receive webhook notificationsverificationToken: Token your endpoint must return during verification
description: Human-friendly explanation of the webhook's purposedisplayName: User-friendly name for the webhook (must be unique per developer) if not provided, it will be set the to the Id of the webhook.status: Initial webhook state ("enabled" or "disabled", defaults to enabled)
CEL (Common Expression Language) conditions determine when webhooks fire. The API validates conditions during webhook creation and provides different variables based on the service type.
For signal webhooks, these variables are available in CEL expressions:
value: Generic value field will either be a number or a string depending on the signal definitionsource: Signal source identifierpreviousValue: Previous signal value as a number or a string depending on the signal definitionpreviousSource: Previous signal source
For location-type signals, additional variables are available:
value.Latitude: Current latitude coordinatevalue.Longitude: Current longitude coordinatevalue.HDOP: Current Horizontal Dilution of PrecisionpreviousValue.Latitude: Previous latitude coordinatepreviousValue.Longitude: Previous longitude coordinatepreviousValue.HDOP: Previous Horizontal Dilution of Precision
Examples:
// Simple threshold
"value > 55";
// Range check
"value >= 10.0 && value <= 50.0";
// Change detection
"valueNumber != previousValueNumber";
// Combine conditions
"valueNumber > 20 && valueNumber != previousValue";
// String conditions
"valueString == 'active'";
// String contains check
"valueString.contains('emergency')";
// Location-based conditions
"value.Latitude > 40.0 && value.Longitude < -70.0";
// GPS accuracy check
"value.HDOP < 5.0";The geoDistance function is available for location-based conditions and calculates the distance in kilometers between two geographic coordinates using the Haversine formula.
Syntax:
geoDistance(lat1, lon1, lat2, lon2);Parameters:
lat1: First location's latitude (decimal degrees)lon1: First location's longitude (decimal degrees)lat2: Second location's latitude (decimal degrees)lon2: Second location's longitude (decimal degrees)
Returns: Distance in kilometers as a floating-point number
Examples:
// Check if vehicle is within 10km of a specific location
"geoDistance(value.Latitude, value.Longitude, 40.7128, -74.0060) < 10.0";
// Detect significant movement from previous location
"geoDistance(value.Latitude, value.Longitude, previousValue.Latitude, previousValue.Longitude) > 5.0";
// Geofencing with accuracy check
"geoDistance(value.Latitude, value.Longitude, 37.7749, -122.4194) <= 1.0 && value.HDOP < 5.0";
// Distance range check (between 50km and 100km from a point)
"geoDistance(value.Latitude, value.Longitude, 51.5074, -0.1278) >= 50.0 && geoDistance(value.Latitude, value.Longitude, 51.5074, -0.1278) <= 100.0";For event webhooks, these variables are available:
name: Event namesource: Event source identifierdurationNs: Event duration in nanosecondsmetadata: Event metadata as stringpreviousName: Previous event namepreviousSource: Previous event sourcepreviousDurationNs: Previous event durationpreviousMetadata: Previous event metadata
Examples:
// Specific event type
"name == 'HarshBraking'";
// Duration threshold
"durationNs > 1000000000";
// Metadata contains
"metadata.contains('emergency')";
// Complex conditions
"name == 'HarshBraking' && source == '0x1234567890abcdef1234567890abcdef12345678' && durationNs > 500";- Return Boolean: All conditions must evaluate to true/false
- Validation: Conditions are validated when creating/updating webhooks
- Performance: Simple conditions perform better than complex ones
- Cross-Type Comparisons: Numeric comparisons work across int/float types
Display names provide user-friendly identification for webhooks and have specific behavior:
- Display names must be unique per developer license
- The uniqueness constraint is case-insensitive
- If no display name is provided, the webhook ID is used as the display name
- This ensures every webhook has a display name
To get a list of available signals for the metricName field, make a GET request to /v1/webhooks/signals. This returns signal definitions including:
- Signal name (for use in metricName)
- Description of what the signal represents
- Unit of measurement
- Data type (determines which CEL variables to use)
Before accepting a webhook registration, the API verifies your endpoint:
- Sends a POST request to your targetURL with
{"verification": "test"} - Expects a 200 response containing your verification token
- Registration fails if verification doesn't succeed within 10 seconds
When a webhook is triggered, a CloudEvent is sent to the targetURL. The subject of the vehicle is the DID of the vehicle that triggered the webhook.
Example of a signal webhook payload:
{
"id": "16392596-22da-4599-a865-b176948069fb", // UUID of the specific payload of the webhook
"source": "vehicle-triggers-api",
"producer": "1fab16e0-3a51-4118-bc3a-6b6d2fecfe13", // UUID of the webhook
"specversion": "1.0", // Version of the spec always 1.0
"subject": "did:erc721:137:0xbA5738a18d83D41847dfFbDC6101d37C69c9B0cF:12345", // DID of the vehicle
"time": "2025-08-13T10:15:07.630545Z", // Timestamp of the cloudevent was created
"type": "dimo.trigger", // Type of the cloudevent always dimo.trigger when sent from a webhook
"datacontenttype": "application/json", // Content type of the data always application/json
"dataversion": "telemetry.signals/v1.0", // Versioning for the data field in the payload.
"data": {
"service": "telemetry.signals", // Service that sent the signal
"metricName": "speed", // Name of the signal/event
"webhookId": "1fab16e0-3a51-4118-bc3a-6b6d2fecfe13", // UUID of the webhook
"webhookName": "Speed Alert", // Display Name of the webhook
"assetDID": "did:erc721:137:0xbA5738a18d83D41847dfFbDC6101d37C69c9B0cF:12345", // DID of the vehicle
"condition": "valueNumber \u003e 20", // Condition that was used to trigger the webhook escaped for json
"signal": {
"name": "speed", // Name of the signal
"unit": "km/h", // Unit of the signal
"timestamp": "2025-08-13T10:15:04.610342Z", // Timestamp of the signal was captured
"source": "0xF26421509Efe92861a587482100c6d728aBf1CD0", // 0xAddress of the connection that produced the signal
"producer": "did:erc721:137:0x9c94C395cBcBDe662235E0A9d3bB87Ad708561BA:4359", // DID of the device that produced the signal
"valueType": "float64", // data type of the value field either float64 or string
"value": 25 // Value of the signal
}
}
}