Salesforce Field Service Integration with Route Optimization API
Prerequisites
To successfully integrate Salesforce Field Service with NextBillion.ai, ensure you have the following prerequisites in place:
-
Salesforce Field Service Account Credentials: You will need valid credentials, including a username and password, to access your Salesforce Field Service account.
-
NextBillion.ai's API Key: Obtain an API key from NextBillion.ai, which serves as the authentication mechanism for accessing their services and data. This key is essential for secure and authorized communication between your systems and NextBillion.ai's APIs.
Generating Access Token
-
Create a Salesforce Account: Begin by setting up a Salesforce account if you haven't already.
-
Create a Custom App: Once your Salesforce account is established, create a custom application within the Salesforce platform.
-
Configure OAuth for Connection: Configure OAuth settings to establish a secure connection. Before testing the connection, keep these important considerations in mind:
-
Specify Callback URL: Ensure that you specify a valid callback URL.
-
Retrieve Consumer Key and Secret: Obtain your Consumer Key and Secret, which are essential for authentication.
-
-
Test the API Connection: Use Postman or a similar tool to test the API connection and generate the access token.
-
Generate API Access Token: After testing the connection successfully, generate the API access token.
-
Verify Basic CRUD Operations: Finally, verify that basic CRUD (Create, Read, Update, Delete) operations are functioning as expected using the generated access token.
Populate Data from Salesforce
To enrich your Salesforce application, you need to populate the following key components:
Service Appointments
Import and integrate service appointment data from your Salesforce application.
Sample API Request
When the following cURL command is executed, it sends a GET request to Salesforce, which responds with the results of the specified query, typically in JSON format. This allows you to retrieve Salesforce data that matches the criteria defined in the query, which can then be used for integration purposes.
1 2 3
# Fetch addresses from Salesforce curl --request GET \ --url https://nextbillionai-dev-ed.my.salesforce.com/services/data/v58.0/query?q=SELECT+AccountId,Address,AppointmentNumber,Id__c +FROM+ServiceAppointment
Sample API Response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
{ "totalSize": 5, "done": true, "records": [ { "attributes": { "type": "ServiceAppointment", "url": "/services/data/v58.0/sobjects/ServiceAppointment/08p8c000001LFxdAAG" }, "AccountId": "0018c00002okLOJAA2", "Address": { "city": "Chennai", "country": "India", "geocodeAccuracy": "Zip", "latitude": 13.03552, "longitude": 80.22181, "postalCode": "600033", "state": "Tamilnadu", "street": "Appasamy Appartments" }, "AppointmentNumber": "SA-0001", "Id__c": 1 }, { "attributes": { "type": "ServiceAppointment", "url": "/services/data/v58.0/sobjects/ServiceAppointment/08p8c000001LIefAAG" }, "AccountId": "0018c00002okLOJAA2", "Address": { "city": "Chennai", "country": "India", "geocodeAccuracy": "Zip", "latitude": 12.92428, "longitude": 80.25026, "postalCode": "600115", "state": "Tamilnadu", "street": "Dr. Shalini Old Age Home" }, "AppointmentNumber": "SA-0002", "Id__c": 2 }, { "attributes": { "type": "ServiceAppointment", "url": "/services/data/v58.0/sobjects/ServiceAppointment/08p8c000001LJOlAAO" }, "AccountId": "0018c00002okLOJAA2", "Address": { "city": "Chennai", "country": "India", "geocodeAccuracy": "Zip", "latitude": 12.79778, "longitude": 80.21113, "postalCode": "603103", "state": "Tamilnadu", "street": "Isha Code Field Villa" }, "AppointmentNumber": "SA-0003", "Id__c": 3 }, { "attributes": { "type": "ServiceAppointment", "url": "/services/data/v58.0/sobjects/ServiceAppointment/08p8c000001LKgHAAW" }, "AccountId": "0018c00002okLOJAA2", "Address": { "city": "Chennai", "country": "India", "geocodeAccuracy": "Zip", "latitude": 12.94937, "longitude": 80.14924, "postalCode": "600044", "state": "Tamilnadu", "street": "Priyanka Appartments" }, "AppointmentNumber": "SA-0006", "Id__c": 6 }, { "attributes": { "type": "ServiceAppointment", "url": "/services/data/v58.0/sobjects/ServiceAppointment/08p8c000001LJx7AAG" }, "AccountId": "0018c00002okLOJAA2", "Address": { "city": "Chennai", "country": "India", "geocodeAccuracy": "Zip", "latitude": 13.01267, "longitude": 80.20937, "postalCode": "600032", "state": "Tamilnadu", "street": "VGN Fairmount" }, "AppointmentNumber": "SA-0005", "Id__c": 5 } ] }
The integrated dashboard will resemble the following image:
Service Resources
Likewise, ensure that service resource information is efficiently integrated and populated within your Salesforce environment. Retrieve a list of your Technicians, which represent your vehicles. This information will be used to optimize routes based on vehicle capacity and other parameters.
Sample API Request
1 2
curl --request GET \ --url https://nextbillionai-dev-ed.my.salesforce.com//services/data/v58.0/query?q=SELECT+id__c,Name+FROM+ServiceResource
Sample API Response
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
{ "totalSize": 2, "done": true, "records": [ { "attributes": { "type": "ServiceResource", "url": "/services/data/v58.0/sobjects/ServiceResource/0Hn8c000000ELyyCAG" }, "id__c": 2, "Name": "Technician 2" }, { "attributes": { "type": "ServiceResource", "url": "/services/data/v58.0/sobjects/ServiceResource/0Hn8c000000ELyjCAG" }, "id__c": 1, "Name": "Technician 1" } ] }
After successfully populating the data from Salesforce, your integerated dashboard will look something like this:
Integration with Route Optimization API
After successfully pulling the necessary data from Salesforce, integrate the fetched data with Route Optimization using the following API endpoint.
Map the fetched Salesforce data to the respective API parameters. Configure optimization settings such as shift start and end time, service time, and start and end location coordinates.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
let vehicles = []; let shifts = []; let starts = []; let ends = []; let veh_attr = []; let stop_attr = []; let shiftStart = (new Date(dtShiftStart2.value).getTime()/1000); let shiftEnd = (new Date(dtShiftEnd2.value).getTime()/1000); // START NBAI PROBLEM BUILD let locations = []; let jobs = []; let shipments = []; vehicles = []; veh_attr = []; stop_attr = []; // sequence through all stops and create job array while // also creating the indexed list of locations let location_index = 0; let location = []; tblStops.data.forEach(planstop => { let job = { id: parseInt(planstop.Id__c), description: planstop.Address.street, location_index: location_index++, service: numberInput1.value, priority: 0, time_windows: [[shiftStart,shiftEnd]] } location.push(`${planstop.Address.latitude},${planstop.Address.longitude}`); jobs.push(job); }); // Add shift start/stop location to location index location.push(`${txtVehicleStartPosition2.value}`); location.push(`${txtVehicleEndPosition2.value}`); location_index += 1; let veh_fixed_cost = 0; tblDrivers.data.forEach( (v,idx) => { let breaks = []; let vehicle = { id: parseInt(v.id__c), start_index: location_index, time_window: [shiftStart, shiftEnd], //skills: v.skills, // breaks: breaks, max_tasks: 25, costs: { fixed: veh_fixed_cost } }; vehicles.push(vehicle); veh_fixed_cost += 0; //} }); locations = { id: 12, description: 'Salesforce Test', location: location }; let options = { routing: { mode: "car" } }; localStorage.setValue('vehiclesNBAI', vehicles); localStorage.setValue('shipmentsNBAI', shipments); localStorage.setValue('jobsNBAI', jobs); localStorage.setValue('locationsNBAI', locations); localStorage.setValue('nbaiOptions', options); localStorage.setValue('nbaiVRPResult', null); nbaiOptimizationRun.trigger();
Once you have successfully retrieved the required data from Salesforce, the next step is to seamlessly integrate this data with the Route Optimization service using the provided API.
Follow these steps to achieve a successful integration:
Step 1: Define Data Structures
Begin by setting up the necessary data structures to facilitate the integration. These include arrays and variables for vehicles, shifts, start and end locations, vehicle attributes, and stop attributes.
1 2 3 4 5 6
let vehicles = []; let shifts = []; let starts = []; let ends = []; let veh_attr = []; let stop_attr = [];
Step 2: Configure Optimization Settings
Specify optimization settings to tailor the routing process to your specific needs. These settings typically include shift start and end times, service times, and the coordinates of start and end locations.
1 2
let shiftStart = (new Date(dtShiftStart2.value).getTime() / 1000); let shiftEnd = (new Date(dtShiftEnd2.value).getTime() / 1000);
Step 3: Prepare Data for Route Optimization
Assemble the data required for route optimization. This involves creating arrays for locations, jobs, and shipments. Additionally, you'll populate the vehicle and job attributes as needed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
let locations = []; let jobs = []; let shipments = []; vehicles = []; veh_attr = []; stop_attr = []; // Iterate through stops to create job array and index locations let location_index = 0; let location = []; tblStops.data.forEach(planstop => { let job = { id: parseInt(planstop.Id__c), description: planstop.Address.street, location_index: location_index++, service: numberInput1.value, priority: 0, time_windows: [[shiftStart, shiftEnd]] }; location.push(`${planstop.Address.latitude},${planstop.Address.longitude}`); jobs.push(job); }); // Add shift start/stop locations to the location index location.push(`${txtVehicleStartPosition2.value}`); location.push(`${txtVehicleEndPosition2.value}`); location_index += 1; // Define vehicle fixed cost and configure vehicles let veh_fixed_cost = 0; tblDrivers.data.forEach((v, idx) => { let breaks = []; let vehicle = { id: parseInt(v.id__c), start_index: location_index, time_window: [shiftStart, shiftEnd], max_tasks: 25, costs: { fixed: veh_fixed_cost } }; vehicles.push(vehicle); veh_fixed_cost += 0; // Adjust fixed cost as needed }); // Create the 'locations' object and 'options' for routing locations = { id: 12, description: 'Salesforce Test', location: location }; let options = { routing: { mode: "car" } };
Step 4: Store Data and Trigger Optimization
Store the prepared data structures in local storage for future reference and initiate the route optimization process.
1 2 3 4 5 6 7 8
localStorage.setValue('vehiclesNBAI', vehicles); localStorage.setValue('shipmentsNBAI', shipments); localStorage.setValue('jobsNBAI', jobs); localStorage.setValue('locationsNBAI', locations); localStorage.setValue('nbaiOptions', options); localStorage.setValue('nbaiVRPResult', null); nbaiOptimizationRun.trigger();
By following these steps, you can effectively connect and integrate Salesforce data with the Route Optimization API, ensuring efficient routing and resource allocation for your tasks or deliveries.
Create Jobs with Route Optimization API
This integration step showcases how to utilize NextBillion.ai's Route Optimization API to create optimization jobs, allowing for efficient route planning and resource allocation. The API response provides critical job information, enabling further interaction and tracking within the integration workflow.
POST Query
To create optimization jobs, a POST request is sent to the designated API endpoint using the curl command-line tool or an equivalent HTTP client.
1 2
curl --request POST \ --url https://api.nextbillion.io/optimization/v2?key={{localStorage.values.apiKey}}
API Request
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
{ "request": { "url": "https://api.nextbillion.io/optimization/v2?key=<your api key>", "method": "POST", "body": "{\"locations\":{\"id\":12,\"description\":\"Salesforce Test\",\"location\":[\"13.03552,80.22181\",\"12.92428,80.25026\",\"12.79778,80.21113\",\"12.94937,80.14924\",\"13.01267,80.20937\",\"13.03552,80.22181\",\"13.03552,80.22181\"]},\"jobs\":[{\"id\":1,\"description\":\"Appasamy Appartments\",\"location_index\":0,\"service\":120,\"priority\":0,\"time_windows\":[[1694658600,1694676600]]},{\"id\":2,\"description\":\"Dr. Shalini Old Age Home\",\"location_index\":1,\"service\":120,\"priority\":0,\"time_windows\":[[1694658600,1694676600]]},{\"id\":3,\"description\":\"Isha Code Field Villa\",\"location_index\":2,\"service\":120,\"priority\":0,\"time_windows\":[[1694658600,1694676600]]},{\"id\":6,\"description\":\"Priyanka Appartments\",\"location_index\":3,\"service\":120,\"priority\":0,\"time_windows\":[[1694658600,1694676600]]},{\"id\":5,\"description\":\"VGN Fairmount\",\"location_index\":4,\"service\":120,\"priority\":0,\"time_windows\":[[1694658600,1694676600]]}],\"vehicles\":[{\"id\":2,\"start_index\":6,\"time_window\":[1694658600,1694676600],\"max_tasks\":25,\"costs\":{\"fixed\":0}},{\"id\":1,\"start_index\":6,\"time_window\":[1694658600,1694676600],\"max_tasks\":25,\"costs\":{\"fixed\":0}}],\"options\":{\"routing\":{\"mode\":\"car\"}},\"result\":null}", "headers": { "Content-Type": "application/json", "User-Agent": "Retool/2.0 (+https://docs.tryretool.com/docs/apis)", "ot-baggage-requestId": "undefined", "x-datadog-trace-id": "3635335836028342090", "x-datadog-parent-id": "3092369415317155632", "x-datadog-sampling-priority": "-1", "traceparent": "00-000000000000000032734eae99b78f4a-2aea4d996242ef30-00", "tracestate": "dd=s:-1", "X-Retool-Forwarded-For": "183.82.29.95" } }, "response": { "data": { "id": "cf7a86c2fd8d6c1b9e87eb87c5af154b", "message": "Optimization job created", "status": "Ok", "warning": [ "location_index[5] is unused" ] }, "headers": { "server": [ "nginx/1.25.2" ], "date": [ "Wed, 13 Sep 2023 09:08:20 GMT" ], "content-type": [ "application/json; charset=utf-8" ], "content-length": [ "134" ], "access-control-allow-origin": [ "*" ], "access-control-allow-headers": [ "authorization" ], "access-control-allow-methods": [ "DELETE,GET,HEAD,PUT,PATCH,POST,OPTIONS" ], "strict-transport-security": [ "max-age=15724800; includeSubDomains" ], "via": [ "1.1 google" ], "alt-svc": [ "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" ] }, "status": 200, "statusText": "OK" } }
The request payload contains essential information needed for the optimization job. It is structured as a JSON object and includes the following elements:
-
locations
: Specifies location data, including IDs, descriptions, coordinates, and time windows. -
jobs
: Defines job details, such as IDs, descriptions, location indices, service times, and time windows. -
vehicles
: Describes vehicle properties, including IDs, starting indices, time windows, and task capacities. -
options
: Specifies optimization settings, such as routing mode. -
result
: Initially set to null, as it awaits the response from the API.
API Response
Upon successfully processing the request, the API responds with a JSON object containing the following details:
1 2 3 4 5
{ "id": "456fd9f88e72cf92aec1bd68cfa81cea", "message": "Optimization job created", "status": "Ok" }
Retrieving Optimized Route Result
To obtain the optimized route results after initiating a route optimization job, you can utilize the provided GET query. This query enables you to fetch the optimized route details from the Route Optimization API.
GET Query
https://api.nextbillion.io/optimization/v2/result?id={{nbaiOptimizationRun.data.id}}&key={{localStorage.values.apiKey}}
In the above URL, two query parameters are used:
-
id
: This parameter specifies the unique identifier associated with the optimization job. It is obtained from thenbaiOptimizationRun.data.id
variable. -
key
: ThelocalStorage.values.apiKey
variable provides the API key necessary for authentication.
Example JSON Response
Upon making the GET request, the API will respond with a JSON object containing the optimized route details. Here's an example of what the JSON response may look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
{ "result": { "code": 0, "summary": { "cost": 6385, "routes": 2, "unassigned": 0, "setup": 0, "service": 600, "duration": 6385, "waiting_time": 0, "priority": 0, "distance": 52046.399999999994 }, "routes": [ { "vehicle": 2, "cost": 0, "steps": [ { "type": "start", "arrival": 1694658600, "duration": 0, "service": 0, "waiting_time": 0, "location": [ 13.03552, 80.22181 ], "location_index": 6 }, { "type": "job", "arrival": 1694658600, "duration": 0, "service": 120, "waiting_time": 0, "location": [ 13.03552, 80.22181 ], "location_index": 0, "id": 1, "description": "Appasamy Appartments" }, { "type": "end", "arrival": 1694658720, "duration": 0, "service": 0, "waiting_time": 0, "location": [ 13.03552, 80.22181 ], "location_index": 0 } ], "service": 120, "duration": 0, "waiting_time": 0, "priority": 0, "distance": 0, "geometry": "o`qnAwhshN????" }, { "vehicle": 1, "cost": 6385, "steps": [ { "type": "start", "arrival": 1694658600, "duration": 0, "service": 0, "waiting_time": 0, "location": [ 13.03552, 80.22181 ], "location_index": 6 }, { "type": "job", "arrival": 1694659264, "duration": 664, "service": 120, "waiting_time": 0, "location": [ 13.01267, 80.20937 ], "location_index": 4, "id": 5, "description": "VGN Fairmount" }, { "type": "job", "arrival": 1694660873, "duration": 2153, "service": 120, "waiting_time": 0, "location": [ 12.94937, 80.14924 ], "location_index": 3, "id": 6, "description": "Priyanka Appartments" }, { "type": "job", "arrival": 1694663107, "duration": 4267, "service": 120, "waiting_time": 0, "location": [ 12.92428, 80.25026 ], "location_index": 1, "id": 2, "description": "Dr. Shalini Old Age Home" }, { "type": "job", "arrival": 1694665345, "duration": 6385, "service": 120, "waiting_time": 0, "location": [ 12.79778, 80.21113 ], "location_index": 2, "id": 3, "description": "Isha Code Field Villa" }, { "type": "end", "arrival": 1694665465, "duration": 6385, "service": 0, "waiting_time": 0, "location": [ 12.79778, 80.21113 ], "location_index": 2 } ], "service": 480, "duration": 6385, "waiting_time": 0, "priority": 0, "distance": 52046.399999999994, "geometry": "o`qnAwhshNh@rBN`@DZDEFAFABAN?D?L?@?D?@?@?D@X@B@P@D@F@B?F@b@DB?...LBHHJNBJBTDpAXl@Lb@JB?Mx@i@~B??" } ] }, "status": "Ok", "message": "" }
Accessing Optimized Routes in NextBillion.ai’s Optimizer
To visualize and analyze the optimized routes generated by NextBillion's Optimizer, you can use the following URL.
https://playground.nextbillion.ai/optimization-tester?apiKey={{localStorage.values.apiKey}}&requestID={{nbaiOptimizationRun.data.id}}
The above URL will take you to NextBillion's Optimizer interface where you can explore and interact with the optimized route results associated with your specific request ID and API key. This provides a convenient way to gain insights into your optimized routes for enhanced decision-making and resource management
By integrating Salesforce Field Service with NextBillion.ai's Route Optimization API, businesses can streamline field operations, reduce costs, and provide better service to customers. This integration enhances efficiency and competitiveness in the market, ultimately leading to improved customer satisfaction.