Skip to main content

Webhooks and client notifications

SWAT systems and services related to real-time operations use a message queue to implement real-time notification of consumers. These notifications cover various events including incoming bookings, vehicle motion, offers, scheduler updates, and more.

In order to leverage the messaging system, clients can subscribe to these messages using webhooks.

Subscribing to messages

Subscriptions to webhooks can be managed by the client at the level of a (simulation

) by setting the webhook address and a filter for the messages the client wishes to receive.

To do this, the simulation can be patched via a request. Alternatively, a simulation template can be used.

Configuration via PATCH

PATCH https://<base_url>/api/v2/simulation/<simulation_id>

danger

When updating objects within the simulation settings, exercise caution, particularly with the data field. The data field is an arbitrary JSON object not associated with a strict model. Sending a request like the one below will replace the entire data object, erasing any existing values.

To safely add a new value to the data property:

  1. Retrieve the current simulation object.
  2. Update the data dictionary with your new fields.
  3. PATCH the simulation with the complete, merged data payload.
Webhooks subscription payload
{
"data": {
"messaging_webhook_url": "https://your-webhook-url.com/endpoint",
"messaging_webhook_message_types": [
"assignment",
"bookings",
"bookings_cancellation",
"fail_to_board",
"offers",
"no_offer",
"node_status",
"offer_accepted",
"offer_rejected",
"vehicle_arrival",
"vehicle_state_changed"
]
}
}
warning

Webhooks currently do not support authentication mechanisms (like HMAC signatures). The recommended way to secure your webhook endpoint is by whitelisting the IP addresses of the SWAT backend services issuing the notifications.

Message Structure

All webhook messages share a common envelope structure. The data field contains the event-specific payload.

Base message schema
{
"simulation_id": 12345,
"agent_type": "scheduler",
"message_type": "assignment",
"agent_id": "optional-uuid-string",
"data": {
"key": "value"
},
"current_sim_ts": "2025-04-01T12:00:00+00:00",
"server_ts": "2025-04-01T12:00:00.123456+00:00",
"transmit_to_websocket": false
}
FieldDescription
simulation_idInteger ID of the simulation generating the event.
agent_typeThe type of agent generating the message (e.g., scheduler, vehicle, event_publisher).
message_typeThe specific type of event (e.g., bookings, assignment).
agent_id(Optional) UUID of the specific agent instance.
dataThe payload of the message. Structure depends on message_type.
current_sim_tsThe timestamp within the simulation logic (in real-time operations, corresponds to current time).
server_tsThe timestamp when the message was processed by the server.
transmit_to_websocketBoolean indicating if this message is intended for WebSocket clients.

Event Types

Booking Events

New Bookings (bookings)

Triggered when new bookings are created.

{
"simulation_id": 123,
"agent_type": "event_publisher",
"message_type": "bookings",
"data": {
"bookings": [
{
"id": 1001,
"uid": "uuid-string",
"pickup_lat": 1.23,
"pickup_lon": 103.45
}
]
},
"current_sim_ts": "2025-04-01T12:00:00+00:00",
"server_ts": "2025-04-01T12:00:00.123+00:00",
"transmit_to_websocket": true
}

Booking Cancellations (bookings_cancellation)

Triggered when bookings are cancelled.

{
"simulation_id": 123,
"agent_type": "event_publisher",
"message_type": "bookings_cancellation",
"data": {
"booking_ids": [1001, 1002]
},
"current_sim_ts": "2025-04-01T12:05:00+00:00",
"server_ts": "2025-04-01T12:05:00.123+00:00",
"transmit_to_websocket": true
}

Fail to Board (fail_to_board)

Triggered when a passenger fails to board (no-show).

{
"simulation_id": 123,
"agent_type": "vehicle",
"message_type": "fail_to_board",
"data": {
"booking_ids": [1001],
"booking_uids": ["uuid-string"]
},
"current_sim_ts": "2025-04-01T12:30:00+00:00",
"server_ts": "2025-04-01T12:30:00.123+00:00",
"transmit_to_websocket": true
}

Offer Events

Offers Generated (offers)

Triggered when offers are generated for bookings.

View Example JSON
{
"simulation_id": 3622,
"agent_type": "event_publisher",
"message_type": "offers",
"data": {
"offer_ids": [60201],
"offers": [
{
"id": 60201,
"uid": "fc57f35c-f254-4831-93fd-b987907eb1ad",
"service_session_id": "78a0034a-dbf1-4f37-8d93-7e97aada432f",
"booking_id": "8d191c2a-2371-4111-9d81-328d20be0e5c",
"vehicle_id": "69b751c0-9b35-46a9-943d-73b68eb76b63",
"offer_pickup_location_lat": 35.705422,
"offer_pickup_location_lon": 139.562104,
"estimated_pickup_time": "2020-07-25T02:13:10.993305+00:00",
"demand": { "passenger": 1 }
}
]
},
"current_sim_ts": "2020-07-25T02:10:50.244+00:00",
"server_ts": "2020-07-25T02:10:50.244+00:00",
"transmit_to_websocket": false
}

Offer Accepted (offer_accepted)

Triggered when a user accepts an offer.

{
"simulation_id": 123,
"agent_type": "event_publisher",
"message_type": "offer_accepted",
"data": {
"offer_id": 60201
},
"current_sim_ts": "2025-04-01T12:10:00+00:00",
"server_ts": "2025-04-01T12:10:00.123+00:00",
"transmit_to_websocket": true
}

Offer Rejected (offer_rejected)

Triggered when an offer is rejected or expires.

{
"simulation_id": 123,
"agent_type": "event_publisher",
"message_type": "offer_rejected",
"data": {
"offer_id": 60201
},
"current_sim_ts": "2025-04-01T12:11:00+00:00",
"server_ts": "2025-04-01T12:11:00.123+00:00",
"transmit_to_websocket": true
}

No Offer (no_offer)

Triggered when the scheduler fails to find a suitable offer for a booking.

{
"simulation_id": 123,
"agent_type": "scheduler",
"message_type": "no_offer",
"data": {
"booking_ids": [6478566],
"booking_uids": ["aa082b4c-e8b3-4caf-a9f2-aba007bdde66"]
},
"current_sim_ts": "2025-04-01T00:32:45.043+00:00",
"server_ts": "2025-04-01T00:33:16.168+00:00",
"transmit_to_websocket": true
}

Scheduler & Assignment Events

Calculation Started (calc_started)

Triggered when the scheduler begins a calculation for a batch of bookings.

{
"simulation_id": 123,
"agent_type": "scheduler",
"message_type": "calc_started",
"data": {
"booking_ids": [101, 102],
"booking_uids": ["uuid-1", "uuid-2"]
},
"current_sim_ts": "2025-04-01T12:00:00+00:00",
"server_ts": "2025-04-01T12:00:00.123+00:00",
"transmit_to_websocket": false
}

Scheduler Result (scheduler_result)

Contains the results of a calculation before assignment (internal/debug).

{
"simulation_id": 123,
"agent_type": "scheduler",
"message_type": "scheduler_result",
"data": {
"nodes": [],
"vehicle_id": 1854642
},
"current_sim_ts": "2025-04-01T12:01:00+00:00",
"server_ts": "2025-04-01T12:01:00.123+00:00",
"transmit_to_websocket": false
}

Assignment (assignment)

Confirmed assignment of a route (nodes) to a vehicle. This is the instruction for the vehicle to execute.

View Example JSON
{
"simulation_id": 158064,
"agent_type": "vehicle_group",
"message_type": "assignment",
"data": {
"vehicle_id": 1854642,
"vehicle_agent_id": "6a955d42-706a-4b54-9562-391bc74fbd01",
"nodes": [
{
"id": 12648515,
"uid": "4c6ed49d-fe30-4243-9683-8e20ff60546d",
"lat": 1.339019,
"lon": 103.852623,
"node_type": "pickup",
"status": "assigned",
"booking_id": 6478564
},
{
"id": 12648516,
"uid": "9eb83008-7c5e-45d0-9743-73d62ede782a",
"lat": 1.329019,
"lon": 103.862623,
"node_type": "dropoff",
"status": "assigned",
"booking_id": 6478564
}
],
"edges": [
{
"from_node_id": 12648515,
"to_node_id": 12648516,
"polyline5": "encoded_polyline_string",
"edge_type": "between_nodes"
}
]
},
"current_sim_ts": "2025-04-01T12:02:00+00:00",
"server_ts": "2025-04-01T12:02:00.123+00:00",
"transmit_to_websocket": true
}

Vehicle Status Events

Vehicle Arrival (vehicle_arrival)

Generated periodically if performance_tracker_enabled is true. Contains ETA information for upcoming nodes.

{
"simulation_id": 158066,
"agent_type": "vehicle",
"message_type": "vehicle_arrival",
"data": {
"booking_id": 6478565,
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"node_id": 12648518,
"node_uid": "89bfed3c-6ec2-43de-b399-2767319aacb3",
"node_type": "dropoff",
"estimated_earliest_arrival_ts": "2025-04-01T00:45:55+00:00",
"estimated_scheduled_ts": "2025-04-01T07:00:00+00:00",
"scheduled_ts": "2025-04-01T07:00:00+00:00",
"vehicle_id": 1854644
},
"current_sim_ts": "2025-04-01T00:39:04+00:00",
"server_ts": "2025-04-01T00:39:04+00:00",
"transmit_to_websocket": false
}

Vehicle Arrival Grouped (vehicle_arrival_grouped)

A list of arrival messages grouped by vehicle.

{
"simulation_id": 1,
"agent_type": "vehicle",
"message_type": "vehicle_arrival_grouped",
"data": {
"vehicle_id": 1,
"vehicle_agent_id": "12345",
"arrivals": [
{
"vehicle_id": 1,
"node_id": 1,
"estimated_earliest_arrival_ts": "2021-02-19T15:27:24+00:00"
}
]
},
"current_sim_ts": "2021-02-03T08:17:09+00:00",
"server_ts": "2021-02-03T08:17:09+00:00",
"transmit_to_websocket": false
}

Node Status (node_status)

Triggered when a vehicle completes a node (e.g., pickup success, dropoff).

{
"simulation_id": 158066,
"agent_type": "vehicle",
"message_type": "node_status",
"data": {
"node_uid": "cc90ed77-88c3-4f79-9aa6-b61d78d5ca44",
"status": "completed",
"vehicle_id": 1854644,
"node_type": "pickup",
"action_data": {
"action_type": "pickup_success",
"data": {
"driver_note": {
"additional_note": "notes"
}
}
}
},
"current_sim_ts": "2025-04-01T14:18:18+00:00",
"server_ts": "2025-04-01T01:18:18+00:00",
"transmit_to_websocket": false
}

Vehicle State Changed (vehicle_state_changed)

Indicates a general change in vehicle state, such as a new route being assigned or a status change.

{
"simulation_id": 158066,
"agent_type": "vehicle",
"message_type": "vehicle_state_changed",
"data": {
"vehicle_id": 1854644,
"instance_changed": true,
"nodes_changed": true,
"update": {
"path": "encoded_polyline_string"
}
},
"current_sim_ts": "2025-04-01T00:34:04+00:00",
"server_ts": "2025-04-01T00:34:04+00:00",
"transmit_to_websocket": false
}

Vehicle States (vehicle_states)

Detailed snapshot of the vehicle's internal state.

{
"simulation_id": 123,
"agent_type": "vehicle",
"message_type": "vehicle_states",
"data": {
"vehicle_states": [
{
"id": 1,
"lat": 1.3,
"lon": 103.8,
"status": "idle",
"current_route": []
}
]
},
"current_sim_ts": "2025-04-01T12:00:00+00:00",
"server_ts": "2025-04-01T12:00:00+00:00",
"transmit_to_websocket": false
}

GPS Data (gps_data)

Raw location update event.

{
"simulation_id": 123,
"agent_type": "vehicle",
"message_type": "gps_data",
"data": {
"vehicle_event": "gps_data",
"vehicle_id": 1756,
"lat": 1.3,
"lon": 103.88,
"bearing": 100,
"speed": 9.8,
"device_ts": "2025-04-01T12:00:00+00:00"
},
"current_sim_ts": "2025-04-01T12:00:00+00:00",
"transmit_to_websocket": true
}

Vehicle Use (vehicle_use)

Triggered when a vehicle is enabled or disabled.

{
"simulation_id": 123,
"agent_type": "vehicle",
"message_type": "vehicle_use",
"data": {
"vehicle_id": 10,
"action": "disabled"
},
"current_sim_ts": "2025-04-01T12:00:00+00:00",
"server_ts": "2025-04-01T12:00:00+00:00",
"transmit_to_websocket": true
}

Demand Modification (demand_modification)

{
"simulation_id": 123,
"agent_type": "vehicle_group",
"message_type": "demand_modification",
"data": {
"vehicle_id": 1,
"node_id": 100,
"tickets": [{ "demand": 1, "ticket_type": "passenger" }]
},
"current_sim_ts": "2025-04-01T12:00:00+00:00",
"server_ts": "2025-04-01T12:00:00+00:00",
"transmit_to_websocket": true
}

Testing Webhooks

You can test webhooks using 3rd-party proxy services like smee.io.

  1. Create a Channel: Go to https://smee.io/ and click "Start a new channel".
  2. Configure Simulation: Update your simulation's messaging_webhook_url with the Smee URL.
  3. Local Proxy (Optional): If you want to process messages locally, install the Smee client:
    npm install --global smee-client
    smee -u https://smee.io/your_token -p 3001
  4. View Messages: You can view the messages directly on the smee.io page or handle them with a local server.

Simple Node.js Logger

Run this script to log incoming webhook payloads to your console.

const http = require('http');

const server = http.createServer((req, res) => {
let body = [];
req.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
try {
body = Buffer.concat(body).toString();
// Try to parse JSON for prettier output if possible, otherwise print string
console.log(`==== ${req.method} ${req.url}`);
console.log('> Headers:', req.headers);
console.log('> Body:', body);
} catch(e) {
console.error("Error processing body", e)
}
res.end();
});
});

server.listen(3001, () => {
console.log('Listening on port 3001...');
});