Webhooks and client notifications
SWAT systems and services, related to real-time operations are using message queue to implement real time notification of the consumers. Messages include:
- Incoming booking message
- Vehicle motion messages
- Offers
- Messages from scheduler
- Other types
In order to leverage the messaging system, clients can subscribe to the messaging system using webhooks.
Subscribing to the messages
When updating objects, exercise caution, particularly with the data field, as it's an arbitrary JSON that's not associated with any model. Sending requests like the one below will erase all other values in the data property. To add a new value to the data property, retrieve the current value, update or add the required field, then PATCH it with the complete payload (i.e., a merger of the existing value with the new values).
Subscriptions to webhooks can be managed by the client at a level of (simulation
) by setting webhooks address and filter for messages the client is wishing to listen to.To achieve that, the simulation can be patched via a request, or, alternatively, simulation template can be used.
PATCH https://<base_url>/api/v2/simulation/<simulation_id>
{
"data": {
"messaging_webhook_url": "https://<webhook_url>",
"messaging_webhook_message_types": ["<message filter>"]
// message type from the list
// ['assignment',
// 'bookings',
// 'bookings_cancellation',
// 'fail_to_board',
// 'offers',
// 'no_offer',
// 'node_status',
// 'offer_accepted',
// 'offer_rejected',
// 'vehicle_arrival',
// 'vehicle_state_changed']
}
}
Webhooks at the moment do not support any authentication. The only way to secure webhooks is through whitelisting the IP address of SWAT backend services issuing the webhook.
Examples of webhook messages
Node status message from vehicle agent
{
"simulation_id": 158066,
"agent_type": "vehicle",
"message_type": "node_status",
"data": {
"node_uid": "cc90ed77-88c3-4f79-9aa6-b61d78d5ca44",
"status": "completed",
"vehicle_id": 1854644,
"node_id": 12648517,
"node_type": "pickup",
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"booking_id": 6478565,
"action_data": {
"action_type": "pickup_success",
"lat": -43.5481311,
"lon": 172.6854358,
"driver_uid": null,
"image_urls": [],
"data": {
"driver_note": {
"additional_note": "notes"
}
}
},
"is_confirmation": true
},
"current_sim_ts": "2025-04-01T14:18:18.293000+13:00",
"server_ts": "2025-04-01T01:18:18.659211+00:00",
"transmit_to_websocket": false,
"agent_id": "3fb96457-6e43-48a9-a678-d47bd398500d",
"user_id": 13529
}
Vehicle arrival message from vehicle agent
{
"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.302476+00:00",
"estimated_scheduled_ts": "2025-04-01T07:00:00+00:00",
"scheduled_ts": "2025-04-01T07:00:00+00:00",
"vehicle_id": 1854644,
"vehicle_agent_id": "884a43a2-cea7-4f42-b705-5fa2bc42bf54"
},
"current_sim_ts": "2025-04-01T00:39:04.002476+00:00",
"server_ts": "2025-04-01T00:39:04.002476+00:00",
"transmit_to_websocket": false,
"agent_id": null,
"user_id": null
}
Vehicle state changed message from vehicle agent
{
"simulation_id": 158066,
"agent_type": "vehicle",
"message_type": "vehicle_state_changed",
"data": {
"vehicle_id": 1854644,
"instance_changed": true,
"nodes_changed": true,
"update": {
"path": "_fvpAmbtaeEbE{ErG_HnToUvTkV~BtBk[h^iKhLiNxO{DnE]^eAlA_FzE{CkCe@c@o@g@sOgMsT_LuJoEaEkBaDmAiLkEqFcBiL}CmHcC{IqBiK_BwCWwDc@{f@eBy{AcGkNr@uCn@wC|@{BvAsB~BoAjD[tC@jDp@xCnAtCtAbBfBnApC|@bCZjCJbCU~Cw@bCiAlCcCdDaDtAyBbCkUj@kRYuO?eOoBuOaB_Yq@cU_@}M]_Ms@uSMcSn@sZ^yXDoHLqGNiW@sI?uH?wFAiDIeFe@yX?wBOwIWoOR}LdAuIjA{HnAsG|@eGp@_Ep@aDV_Ad@gAv@mAdA}@jAs@p@Wp@YjBu@vBw@|F_BjUmFrgBab@z~@iKjYkFp_@uFt_@qDdDSdEQpe@cAvYi@l]u@lUk@|~@Yzq@kAdQc@tj@mA`LGfhBeClkBChFcCjDQ|CLlETtJU`Kw@tCs@"
}
},
"current_sim_ts": "2025-04-01T00:34:04.045211+00:00",
"server_ts": "2025-04-01T00:34:04.045211+00:00",
"transmit_to_websocket": false,
"agent_id": null,
"user_id": null
}
Booking rejected (no offer) message from scheduler agent and no_offer message type
{
"simulation_id": 158066,
"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.043985+00:00",
"server_ts": "2025-04-01T00:33:16.168066+00:00",
"transmit_to_websocket": true,
"agent_id": null,
"user_id": null
}
Booking (nodes) assigned to a vehicle message through vehicle_group agent and message_type assignment
{
"simulation_id": 158064,
"agent_type": "vehicle_group",
"message_type": "assignment",
"data": {
"vehicle_id": 1854642,
"vehicle_agent_id": "6a955d42-706a-4b54-9562-391bc74fbd01",
"polyline5": "uodGwuzxRRUX[dAgAdAkAJJyAbBg@j@q@r@QTC@EFUTOMCACCu@m@eAi@c@SSKOGi@SWIk@O]Ka@Kg@IOAQC_CIgHYq@DMBODKFKLGNAN?PBLFNHHHFLDL@L@JANELGLKNOHMJeAD}@Cu@?s@Iu@IqAEgAAo@Ck@CaAA_ABwABsA?]@[@mA?a@?]?W?QAUCsA?KAa@As@@m@Fa@F_@FYDYBSDO@EBGDGDEFEBADAHEJEXGfAWlIoBjEg@tAWfBWfBQNARAzBEtAC~AEfAClEC`DEx@CjCGh@AnIKzI?TMPAN@R@d@Ad@ENE",
"polyline6": "_fvpAmbtaeEbE{ErG_HnToUvTkV~BtBk[h^iKhLiNxO{DnE]^eAlA_FzE{CkCe@c@o@g@sOgMsT_LuJoEaEkBaDmAiLkEqFcBiL}CmHcC{IqBiK_BwCWwDc@{f@eBy{AcGkNr@uCn@wC|@{BvAsB~BoAjD[tC@jDp@xCnAtCtAbBfBnApC|@bCZjCJbCU~Cw@bCiAlCcCdDaDtAyBbCkUj@kRYuO?eOoBuOaB_Yq@cU_@}M]_Ms@uSMcSn@sZ^yXDoHLqGNiW@sI?uH?wFAiDIeFe@yX?wBOwIWoOR}LdAuIjA{HnAsG|@eGp@_Ep@aDV_Ad@gAv@mAdA}@jAs@p@Wp@YjBu@vBw@|F_BjUmFrgBab@z~@iKjYkFp_@uFt_@qDdDSdEQpe@cAvYi@l]u@lUk@|~@Yzq@kAdQc@tj@mA`LGfhBeClkBChFcCjDQ|CLlETtJU`Kw@tCs@",
"nodes": [
{
"id": 12648515,
"created_at": "2025-03-31T23:23:24.761384+00:00",
"modified_at": "2025-03-31T23:23:24.961358+00:00",
"uid": "4c6ed49d-fe30-4243-9683-8e20ff60546d",
"lat": 1.339019,
"lon": 103.852623,
"h3": "0040062440662254514161",
"location_name": "Hub",
"location_code": "",
"display_name": "Hub",
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"booking_id": 6478564,
"customer_id": null,
"open_time_ts": "2026-02-05T03:00:00+00:00",
"close_time_ts": "2026-02-05T05:00:00+00:00",
"close_time_ts_dynamic": "2026-02-05T05:00:00+00:00",
"service_time": 0,
"demand": {
"sample": 1
},
"status": "assigned",
"node_type": "pickup",
"assigned_vehicle_id": 1854642,
"simulation_id": 158064,
"assignment_order": 0,
"scheduled_ts": "2026-02-05T03:00:00+00:00",
"visited_at_ts": null,
"completed_service_at": null,
"started_service_at_ts": null,
"walking_time_to_node": 0,
"walking_distance_to_node": 0,
"min_trip_duration": null,
"max_trip_duration": 36000,
"fixed_route_walk_time": null,
"fixed_route_ride_time": null,
"fixed_route_wait_time": null,
"fixed_route_journey_time": null,
"fixed_route_is_unreachable": false,
"cancelled_at_ts": null,
"penalty": 10000000,
"lifo_order_check": false,
"lifo_order_penalty": null,
"groups": [],
"matrix_timestamp": "2026-02-05T03:00:00+00:00",
"geofence_ids": [],
"geofence_id": null,
"transit_stop_id": null,
"transit_stop_street": null,
"transit_stop_type": null,
"transfer_type": "depart_at",
"weight": null,
"trip_cost": 0,
"initial_open_time_ts": null,
"initial_max_trip_duration": null,
"initial_close_time_ts": null,
"initial_close_time_ts_dynamic": null,
"max_slack": null,
"slack": 14002.3,
"data": {},
"boarding_pass": "900",
"partial_route_index": null,
"finalization_type": "min",
"allow_jump": false,
"estimated_earliest_arrival_ts": null,
"estimated_scheduled_ts": null,
"vehicle_characteristics": {},
"offer_should_be_auto_accepted": null,
"dynamic_break": null,
"user_accepted_offer_at": null,
"is_invalidated": null,
"time_windows": null
},
{
"id": 12648516,
"created_at": "2025-03-31T23:23:24.761601+00:00",
"modified_at": "2025-03-31T23:23:24.961358+00:00",
"uid": "9eb83008-7c5e-45d0-9743-73d62ede782a",
"lat": 1.329019,
"lon": 103.862623,
"h3": "0040062440662066236013",
"location_name": "254 Some Ave, Some city, some country, 00000",
"location_code": "",
"display_name": "254 Some Ave, Some city, some country, 00000",
"booking_uid": "aa082b4c-e8b3-4caf-a9f2-aba007bdde63",
"booking_id": 6478564,
"customer_id": null,
"open_time_ts": "2026-02-05T07:00:00+00:00",
"close_time_ts": "2026-02-05T13:00:00+00:00",
"close_time_ts_dynamic": "2026-02-05T13:00:00+00:00",
"service_time": 300,
"demand": {
"sample": -1
},
"status": "assigned",
"node_type": "dropoff",
"assigned_vehicle_id": 1854642,
"simulation_id": 158064,
"assignment_order": 1,
"scheduled_ts": "2026-02-05T07:00:00+00:00",
"visited_at_ts": null,
"completed_service_at": null,
"started_service_at_ts": null,
"walking_time_to_node": 0,
"walking_distance_to_node": 0,
"min_trip_duration": null,
"max_trip_duration": 36000,
"fixed_route_walk_time": null,
"fixed_route_ride_time": null,
"fixed_route_wait_time": null,
"fixed_route_journey_time": null,
"fixed_route_is_unreachable": false,
"cancelled_at_ts": null,
"penalty": 10000000,
"lifo_order_check": false,
"lifo_order_penalty": null,
"groups": [],
"matrix_timestamp": "2026-02-05T13:00:00+00:00",
"geofence_ids": [],
"geofence_id": null,
"transit_stop_id": null,
"transit_stop_street": null,
"transit_stop_type": null,
"transfer_type": "depart_at",
"weight": null,
"trip_cost": 0,
"initial_open_time_ts": null,
"initial_max_trip_duration": null,
"initial_close_time_ts": null,
"initial_close_time_ts_dynamic": null,
"max_slack": null,
"slack": 0,
"data": {},
"boarding_pass": "863",
"partial_route_index": null,
"finalization_type": "min",
"allow_jump": false,
"estimated_earliest_arrival_ts": null,
"estimated_scheduled_ts": null,
"vehicle_characteristics": {},
"offer_should_be_auto_accepted": null,
"dynamic_break": null,
"user_accepted_offer_at": null,
"is_invalidated": null,
"time_windows": null
}
],
"edges": [
{
"from_node_id": 12648515,
"to_node_id": 12648516,
"polyline5": "uodGwuzxRRUX[dAgAdAkAJJyAbBg@j@q@r@QTC@EFUTOMCACCu@m@eAi@c@SSKOGi@SWIk@O]Ka@Kg@IOAQC_CIgHYq@DMBODKFKLGNAN?PBLFNHHHFLDL@L@JANELGLKNOHMJeAD}@Cu@?s@Iu@IqAEgAAo@Ck@CaAA_ABwABsA?]@[@mA?a@?]?W?QAUCsA?KAa@As@@m@Fa@F_@FYDYBSDO@EBGDGDEFEBADAHEJEXGfAWlIoBjEg@tAWfBWfBQNARAzBEtAC~AEfAClEC`DEx@CjCGh@AnIKzI?TMPAN@R@d@Ad@ENE",
"edge_type": "between_nodes"
}
]
},
"current_sim_ts": "2025-03-31T23:23:24.808778+00:00",
"server_ts": "2025-03-31T23:23:55.838428+00:00",
"transmit_to_websocket": true,
"agent_id": null,
"user_id": null
}
Types of the messages and the schemas
{
"simulation_id": Integer simulation identity,
"agent_type": String agent type, [scheduler, vehicle_group, other],
"message_type": String about message type,
"agent_id": String agent_id, # optional key
"data": {}, dictionary, meaningful data.
"current_sim_ts": simulation timestamp (in real operations, current time),
"server_ts": server timestamp,
"transmit_to_websocket": whether to transmit to websocket this message
}
Types of message filters supported
Agents that can generate messages:
Agent types:
#: Event publisher - publishes different independent events.
EVENT_PUBLISHER = 'event_publisher'
#: Scheduler - schedules tasks for the vehicle groups (batch calculator).
SCHEDULER = 'scheduler'
#: State consumer - consumes events from system. And can do something,
for example, dump them into database.
STATE_CONSUMER = 'state_consumer'
#: Vehicle agent.
VEHICLE = 'vehicle'
#: Vehicle group is a collection of vehicles.
VEHICLE_GROUP = 'vehicle_group'
Event publisher agent
New bookings message.
{
'simulation_id': id,
'agent_type': 'event_publisher',
'message_type': 'bookings',
'agent_id': String agent_id, # optional key
'data': {
'bookings': [array of booking objects]
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Booking cancellations message.
{
'simulation_id': id,
'agent_type': 'event_publisher',
'message_type': 'bookings_cancellation',
'agent_id': String agent_id, # optional key
'data': {
'booking_ids': [array of booking ids integers]
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Offer accepted message.
{
'simulation_id': id,
'agent_type': 'event_publisher',
'agent_id': String agent_id, # optional key
'message_type': 'offer_accepted',
'data': {
'offer_id': offer_id
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Offer rejected message
{
'simulation_id': id,
'agent_type': 'event_publisher' or 'scheduler',
'message_type': 'offer_rejected',
'agent_id': String agent_id, # optional key
'data': {
'offer_id': offer_id
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Scheduler agent
Scheduler start calculation message
{
'simulation_id': id,
'agent_type': 'scheduler',
'message_type': 'calc_started',
'agent_id': String agent_id,
'data': {
'booking_ids': [int], # list of sgerp booking ids
'booking_uids': [str], # list of lakhesis booking uids
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Scheduler calculation result message
{
'simulation_id': id,
'agent_type': 'scheduler',
'message_type': 'scheduler_result', // only calculated nodes, nothing assigned
'agent_id': String agent_id, # optional key
'data': {
'nodes': [array of node objects],
'vehicle_id': vehicle.id, [ID of the vehicle which receives assignments],
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Confirmed assignment message
For particular vehicle_id message. Vehicle should follow this assignment.
{
'simulation_id': simulation.id,
'agent_type': 'scheduler',
'message_type': 'assignment', // this assignment has been assigned to a vehicle
'agent_id': String agent_id, # optional key, scheduler's agent_id
'data': {
'assignment_id': assignment ID,
'nodes': [array of node objects],
'edges': [
{
"from_node_id": null, // because of type vehicle_to_first
"to_node_id": 1,
"polyline5": "encoded_polyline", // with precision 5
"edge_type": "vehicle_to_first"
},
{
"from_node_id": 1,
"to_node_id": 2,
"polyline5": "encoded_polyline", // with precision 5
"edge_type": "between_nodes"
}
],
'vehicle_id': vehicle.id, [ID of the vehicle which receives assignments],
'vehicle_agent_id': vehicle.agent_id,
'polyline5': 'wqeqwewq5', # polyline of complete route with precision 5
'polyline6': 'wqeqwewq6' # polyline of complete route with precision 6
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Generated offer for a particular booking[s].
{
"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,
"offer_pickup_location_type": "bus_stop",
"offer_pickup_location_info": "かたらいの道",
"offer_pickup_display_name": "かたらいの道 #Tokyo_Demo_21",
"offer_pickup_location_street_name": "都武蔵野市中町2丁目11−9",
"streetview_pickup_position_lat": 35.705422,
"streetview_pickup_position_lon": 139.562104,
"streetview_pickup_bearing": 0,
"streetview_pickup_pitch": 0,
"pickup_stop_id": 1109949, # can be null for waypoint-less operations. See H3 index also.
"pickup_walking_distance": 120,
"pickup_walking_time": 120,
"pickup_walking_route": "cr|xEamyrYDMECGGAJ[jAaAfDOG",
"estimated_pickup_time": "2020-07-25T02:13:10.993305+00:00",
"min_pickup_time": "2020-07-25T02:12:38.185137+00:00",
"max_pickup_time": "2020-07-25T02:25:37.915305+00:00",
"boarding_pass": "677",
"offer_dropoff_location_lat": 35.705833,
"offer_dropoff_location_lon": 139.560521,
"offer_dropoff_location_type": "bus_stop",
"offer_dropoff_location_info": "武蔵野警察署前",
"offer_dropoff_display_name": "武蔵野警察署前 #Tokyo_Demo_22",
"offer_dropoff_location_street_name": "都武蔵野市中町1丁目11",
"streetview_dropoff_position_lat": 35.705833,
"streetview_dropoff_position_lon": 139.560521,
"streetview_dropoff_bearing": 0,
"streetview_dropoff_pitch": 0,
"dropoff_stop_id": 1109950, # can be null for waypoint-less operations. See H3 index also.
"dropoff_walking_distance": 69,
"dropoff_walking_time": 69,
"dropoff_walking_route": "mx|xEg|xrYGA@Il@eCB@",
"estimated_dropoff_time": "2020-07-25T02:17:08.393305+00:00",
"min_dropoff_time": "2020-07-25T02:10:37.915305+00:00",
"max_dropoff_time": "2020-07-25T02:41:24.097582+00:00",
"min_trip_duration": 115,
"max_trip_duration": 825,
"demand": {
"passenger": 1
},
"pickup_h3": "0040054340320265516504", // H3 index of the pickup node. Introduced in March 2021
"dropoff_h3": "0040054340320265516504" // H3 index of the dropoff node. Introduced in March 2021
}
]
},
"current_sim_ts": "2020-07-25T02:10:50.244417+00:00",
"server_ts": "2020-07-25T02:10:50.244417+00:00",
"transmit_to_websocket": false,
"agent_id": null
}
No offer message
{
'simulation_id': id,
'agent_type': 'scheduler',
'message_type': 'no_offer',
'agent_id': String agent_id, # scheduler agent_id
'data': {
'booking_ids': [booking.id], # array of booking ids from sgerp
'booking_uids': [booking.uid], # array of booking string uids from lakhesis
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
GPS Data event
(location event from API (or mobile device))
{
"simulation_id": id,
"agent_type": "vehicle",
'agent_id': String agent_id, # optional key
"data": {
"vehicle_event": "gps_data",
"vehicle_id": 1756,
"fleetman_vehicle_id": 10, # fleetman vehicle ID (physical vehicle) optional
"lat": 1.3,
"lon": 103.88,
"bearing": 100,
"speed": 9.8
"device_ts": datetime
},
"current_sim_ts": simulation timestamp,
"transmit_to_websocket": whether to transmit to sgerp websocket this message
}
Vehicle agent
Vehicle states message
{
'simulation_id': simulation_id,
'message_type': 'vehicle_states',
'agent_type': 'vehicle',
'agent_id': String agent_id, # optional key
'data': {
'vehicle_states': [
'id': self.id,
'capacity': self.capacity,
'lat': self.lat,
'lon': self.lon,
'speed': self.speed,
'bearing': self.bearing,
'current_route': self.current_route,
'routing_engine_settings': self.routing_engine_settings,
'routing_engine_url': self.routing_engine_settings.get('url', ''),
'routing_engine_name': self.routing_engine_settings['routing_engine_name'],
'last_assignment_ts': self.last_assignment_ts,
'should_reach_for': self.should_reach_for,
'should_reach_at': self.should_reach_at,
'should_wait_until': self.should_wait_until,
'ts': timezone.now(),
'status': self.status.value,
'total_mileage': self.total_mileage if self.total_mileage else 0,
'path': self.path,fail_to_deliver
'vehicle_cost': self.vehicle_cost,
'lifo_order_check': self.lifo_order_check,
'physical_vehicle_id': self.physical_vehicle_id,
'agent_type': 'vehicle',
'agent_id': self.agent_id,
'completed_nodes': [], # always an empty array now
'assigned_nodes': [node.to_dict() for node in self.assigned_nodes],
'next_waypoint_node': self.next_waypoint_node.to_dict() if self.next_waypoint_node else None,
'total_mileage': self.total_mileage if self.total_mileage else 0,
'ts': timezone.now(),
]
},
'current_sim_ts': current_sim_ts,
'transmit_to_websocket': whether to transmit_to_websocket
}
Node status updates
{
'simulation_id': id,
'agent_type': 'vehicle',
'message_type': 'node_status',
'agent_id': sender messaging agent id,
'data': {
'node_id': id of the node,
'node_uid': uid of the node,
'status': 'completed', # or 'in_service' for more precise use cases
'vehicle_id': integer vehicle id,
'node_type': 'pickup', 'dropoff', 'point'; optional field
'booking_uid': UUID of the node.booking_uid,
'booking_id': integer booking ID for additional reference
},
'current_sim_ts': timestamp of the event,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit_to_websocket
}
Booking fail to board message
{
'simulation_id': id,
'agent_type': 'vehicle',
'message_type': 'fail_to_board',
'agent_id': String agent_id, # optional key
'data': {
'booking_ids': [array of booking ids integers who had no-show],
'booking_uids': [array of booking uids UUIDs who had no-show]
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Vehicle scheduling usability event
Event can be triggered if vehicle should be enabled/disabled for current scheduling
{
'simulation_id': id,
'agent_type': 'vehicle',
'message_type': 'vehicle_use',
'agent_id': String agent_id, # optional key
'data': {
'vehicle_id': integer vehicle id
'action': 'enabled' or 'disabled' # disable is, for example, for vehicle breakdown
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Vehicle arrival message
Vehicle arrival messages are generated for real operations simulations with attribute performance_tracker_enabled set to true:
simulation.performance_tracker_enabled: true
Then, system is generating following messages periodically (about 1 time per 5 minutes, depending on system set up)
{
'simulation_id': id,
'agent_type': 'vehicle',
'message_type': 'vehicle_arrival',
'agent_id': String agent_id, # optional key
'data': {
'vehicle_id': integer vehicle id
'vehicle_agent_id': UUID Vehicle.agent_id
'booking_id': integer SGERP booking ID
'booking_uid': UUID booking UID
'node_id': integer node ID
'node_uid': node UID
'estimated_earliest_arrival_ts': timestamp of possible earliest arrival (not taking into account service times along the route and earliness, so less precise)
'estimated_scheduled_ts': estimated time of starting servicing the node** (taking into account service times along the route and earliness, so **more precise**)
'scheduled_ts': scheduled timestamp, so client can easy compare scheduled and ETA in single message
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Vehicle arrivals grouped message
Vehicle arrivals grouped message is a list of Vehicle arrival messages grouped by vehicle_id
{
"simulation_id": 1,
"agent_type": "vehicle",
"message_type": "vehicle_arrival_grouped",
"data": {
"vehicle_id": 1,
"vehicle_agent_id": "12345",
"arrivals": [
{
"vehicle_id": 1,
"vehicle_agent_id": "12345",
"booking_id": 1,
"booking_uid": "c565534f-29a5-4ed7-9556-8c819e0b6265",
"node_id": 1,
"node_uid": "73f466b4-3162-42f7-b696-a034af3f63c8",
"estimated_earliest_arrival_ts": "2021-02-19T15:27:24.514985+00:00",
"estimated_scheduled_ts": "2021-02-19T15:27:24.514985+00:00",
"scheduled_ts": "2021-02-03T08:17:09.920827+00:00"
},
{
"vehicle_id": 1,
"vehicle_agent_id": "12345",
"booking_id": 1,
"booking_uid": "c565534f-29a5-4ed7-9556-8c819e0b6265",
"node_id": 2,
"node_uid": "a97048d2-f509-41ec-a1fc-2258c3a05003",
"estimated_earliest_arrival_ts": "2021-02-19T15:27:24.514985+00:00",
"estimated_scheduled_ts": "2021-02-19T15:27:24.514985+00:00",
"scheduled_ts": "2021-02-03T08:27:09.925693+00:00"
}
]
},
"current_sim_ts": "2021-02-03T08:17:09.942437+00:00",
"server_ts": "2021-02-03T08:17:09.942437+00:00",
"transmit_to_websocket": false,
"agent_id": null
}
Vehicle group agent
Vehicle state changed message
VehicleStateChangedMessage just contains vehicle ID. It's a signal that something has changed in vehicle state. For example there may be new route.
{
'simulation_id': id,
'agent_type': 'vehicle_group',
'message_type': 'vehicle_arrival',
'agent_id': String agent_id, # optional key
'data': {
'vehicle_id': integer vehicle id
},
'current_sim_ts': simulation timestamp,
'server_ts': server timestamp,
'transmit_to_websocket': whether to transmit to sgerp websocket this message
}
Demand modification message
{
"simulation_id": simulation_id,
"agent_type": "vehicle_group",
"message_type": "demand_modification",
"agent_id": String agent_id, # optional key
"data": {
"vehicle_id": integer vehicle id,
"node_id": integer node id
"tickets": [{"demand": 1, "ticket_type": "passenger"}]
},
"current_sim_ts": simulation timestamp,
"server_ts": server timestamp,
"transmit_to_websocket": whether to transmit to sgerp websocket this message
}
Testing webhooks
Testing of webhooks is possible with the use of some 3rd-party services that are allowing for proxying webhooks requests elsewhere. The process may look like this one:
- Create a webhook proxy url using https://smee.io/ by clicking "Start a new channel"
- Use
collection to configure webhooks for your chosen simulation by putting
https://smee.io/<your_token>intowebhook_urlfield and setting up webhooks filter - Follow smee instructions to install nodejs and npm; setup redirect for smee proxy to localhost by using
smee -u https://smee.io/<your_token> -p 3001in the console - Use smee web browser window to view incoming messages (deliveries), or, alternatively,
- Run nodejs proxy to dump messages into console by using node script in another console
node -e "const h = require('http');const s = h.createServer();const l = console.log;s.on('request', (rq, rs) => {let b = [];rq.on('data', (c) => {b.push(c);}).on('end', () => {b = Buffer.concat(b).toString();l('==== '+rq.method+' '+rq.url);l('> Headers');l(rq.headers);l('> Body');l(b);rs.end();});}).listen(3001);"
- You should start receiving messages in the console from simulations when events occur. You can test the setup by doing POST request to your smee url
https://smee.io/<your_token>with some arbitrary payload, that should appear on the console.