Back to Documentation Overview

How to Add On-demand Delivery to your Ridehail Service

rideOS recently launched Adapt Delivery, a free program providing delivery services for local businesses. In addition, we also announced our partnership with Alto, to support both ride share and delivery services in Dallas. Starting with this post, I’ll show how you can leverage rideOS fleet optimization APIs to satisfy several delivery use cases, while still maintaining your customized frontend UI. In the first part of the series, I’ll discuss on-demand delivery.

On-Demand Delivery

The concept behind on-demand delivery is quite simple: you send each delivery request to a driver whenever a customer creates an order. This is called “on-demand” because you do not know when these delivery requests will come in and have no idea where the pick up and drop off location will actually be. One can design an on-demand system for food, grocery, prescription and other goods.

rideOS provides routing, fleet optimization and ridehail APIs that can easily manage your mobility business. I’ll demonstrate how to leverage rideOS’ RideHail API to satisfy two different delivery requests, each with a different pick up and drop off location and assuming only one vehicle in the fleet. The scenario looks like this:

  1. Customer Peter orders a Deluxe Sandwich from Chick-fil-A on your app and requests home delivery.
  2. rideOS assigns Peter’s request to Vehicle A.
  3. While Vehicle A is on its way to pick up the sandwich for Peter, customer Felix also orders 2 burritos from Chipotle on your app. His request is also dispatched to Vehicle A because it has capacity.
  4. Based on the updated requests in the list, rideOS’ fleet optimization algorithm minimizes total travel time and selects the optimal path to first pick up Peter’s order at Chick-fil-A, then pick up Felix’s order at Chipotle, then drop off Peter’s sandwich then finally drop off Felix’s burritos.

Now, let me show you the code!

Initial setup

Before we start taking customer requests, we need to create a fleet and add Vehicle A to it. We only need to set this up once for each fleet.

API Key

You can sign up for one here and view it on your profile page, then assign it to the API_KEY variable below:

# IMPORTANT: replace "YOUR_RIDEOS_API_KEY" with your actual rideOS API key

API_KEY = "YOUR_RIDEOS_API_KEY"
AUTHORIZATION_HEADER = {"X-Api-Key": API_KEY}

# Creating a fleet.

fleet_id = str(uuid.uuid4())
CREATE_FLEET_URL = "https://api.rideos.ai/ride-hail-operations/v1/CreateFleet"

create_fleet_request = {
  "id": fleet_id,
  "info": {
    "display_name": "Sample On-Demand Food Delivery Fleet"
  }
}

response = requests.post(
  CREATE_FLEET_URL,
  headers=AUTHORIZATION_HEADER,
  json=create_fleet_request
)

response.raise_for_status()
create_fleet_response = response.json()

# Create a vehicle and add it to a fleet. Each vehicle can only belong to one fleet.

vehicle_id = str(uuid.uuid4())
CREATE_VEHICLE_URL = "https://api.rideos.ai/ride-hail-driver/v1/CreateVehicle"

create_vehicle_request = {
  "id": vehicle_id,
  "fleet_id": fleet_id,
  "definition": {
    "rider_capacity": 4
  },
  "info": {
    "driver_info": {
      "contact_info": {
        "name": "The Great Delivery App driver1",
        "phone_number": 987654321
      }
    },
    "properties": {
      "make": "Toyota",
      "model": "Camry"
    }
  }
}

response = requests.post(
  CREATE_VEHICLE_URL,
  headers=AUTHORIZATION_HEADER,
  json=create_vehicle_request
)

response.raise_for_status()
create_vehicle_response = response.json()

# setting the vehicle’s current position by updating the vehicle state

UPDATE_VEHICLE_STATE_URL = "https://api.rideos.ai/ride-hail-driver/v1/UpdateVehicleState"

update_vehicle_state_request = {
  "id": vehicle_id,
  "update_position": {
    "updated_position": { # San Francisco
      "latitude": 37.788897,
      "longitude": -122.389669
    },
    "updated_heading": 0.0
  }
}

response = requests.post(
  UPDATE_VEHICLE_STATE_URL,
  headers=AUTHORIZATION_HEADER,
  json=update_vehicle_state_request
)

response.raise_for_status()
update_vehicle_state_response = response.json()

# Marking the vehicle to accept rides by updating the vehicle state

UPDATE_VEHICLE_STATE_URL = "https://api.rideos.ai/ride-hail-driver/v1/UpdateVehicleState"
update_vehicle_state_request = {
  "id": vehicle_id,
  "set_to_accept_rides": {}
}

response = requests.post(
  UPDATE_VEHICLE_STATE_URL,
  headers=AUTHORIZATION_HEADER,
  json=update_vehicle_state_request
)

response.raise_for_status()
update_vehicle_state_response = response.json()

# Checking the vehicle state. The plan is empty now since the vehicle has no assigned trips yet.

GET_VEHICLE_STATE_URL = "https://api.rideos.ai/ride-hail-driver/v1/GetVehicleState"
get_vehicle_state_request = {
  "id": vehicle_id
}

response = requests.post(
  GET_VEHICLE_STATE_URL,
  headers=AUTHORIZATION_HEADER,
  json=get_vehicle_state_request
)

response.raise_for_status()
get_vehicle_state_response = response.json()

print("Got successful get vehicle state response:")
print(json.dumps(get_vehicle_state_response, indent=4))

# Got successful get vehicle state response:

# {
#   "state": {
#     "position": {
#       "latitude": 37.78898783770332,
#       "longitude": -122.38959319031967
#     },
#     "heading": 0.0,
#     "lastActiveTime": "2020-04-22T00:55:59.112Z",
#     "plan": {
#       "step": []
#     },
#     "readiness": true,
#     "isReachable": true
#   }
# }

You are now ready to handle your first order!

Step 1 to 2: Handling the First Order

Customer Peter orders the Deluxe Sandwich using your app, and the delivery request is assigned to Vehicle A based on rideOS’ fleet optimization algorithm.

# requesting a delivery with fleet id, rider’s id, pick up location and destination location
# in this example we are using a random UUID as peter’s rider_id, feel free to set this to
# any pre-existing user id in your system

rider_peter_id = str(uuid.uuid4())

# we will also assume the chick-fil-A lat long has been calculated elsewhere
# and passed into this method

trip_response, trip_id = request_trip(fleet_id, rider_peter_id, chickfilA_latlong, rider_peter_home_latlong)

def request_trip(fleet_id, rider_id, pickup_location, dropoff_location):
  REQUEST_TRIP_URL = "https://api.rideos.ai/ride-hail-rider/v1/RequestTrip"
  delivery_id = str(uuid.uuid4())
  request_trip_request = {
    "id": delivery_id,
    "rider_id": rider_id,
    "fleet_id": fleet_id,
    "definition": {
      "pickup_dropoff": {
        "pickup": {
          "position": {
            "latitude": pickup_location.lat,
            "longitude": pickup_location.long
          }
        },
        "dropoff": {
          "position": {
            "latitude": dropoff_location.lat,
            "longitude": dropoff_location.long
            }
        },
        "rider_count": 1
      },
    "info": {
      "rider_info": {
        "contact_info": {
          "name": "peter",
          "phone_number": 123456789
        }
      }
    }
  },
}

response = requests.post(
  REQUEST_TRIP_URL,
  headers=AUTHORIZATION_HEADER,
  json=request_trip_request
)

response.raise_for_status()
request_trip_response = response.json()
print("Got successful request trip response:")
return request_trip_response, trip_id

Step 3: Route to Pick Up the First Order

Based on the pickup and dropoff locations, rideOS automatically plans the best route for Vehicle A.

# Checking on the state of the vehicle. If you scroll through the plan of the vehicle, you'll notice the plan looks like drive, pickup, drive, dropoff.
# Something to note: if GetVehicleState is called really quickly after RequestTrip, we might not have a vehicle assigned yet. In that situation, wait a couple seconds and re-run the cell.

GET_VEHICLE_STATE_URL = "https://api.rideos.ai/ride-hail-driver/v1/GetVehicleState"

get_vehicle_state_request = {
  "id": vehicle_id
}

response = requests.post(
  GET_VEHICLE_STATE_URL,
  headers=AUTHORIZATION_HEADER,
  json=get_vehicle_state_request
)

response.raise_for_status()
get_vehicle_state_response = response.json()
print("Got successful get trip state response:")
print(json.dumps(get_vehicle_state_response, indent=4))

Step 4 to 5: Handling the Second Order

rideOS assigns Vehicle A to handle Felix’s request since Vehicle A has enough capacity to pick up another item.

# in this example we are using a random uuid as Felix’s rider_id, feel free to set this to
# any pre-exist user_id in your system

rider_felix_id = str(uuid.uuid4())

# we can reuse the request_trip method that we’ve created earlier !
# we will also assume the chipotle lat long has been calculated elsewhere and
# passed into this method

trip_response, trip_id = request_trip(fleet_id, rider_felix_id, chipotle_latlong, rider_felix_home_latlong)

Step 6 to 9: Merging Two Deliveries with the Optimal Route

rideOS dynamically changes Vehicle A’s route based on newly-added requests. In this case, after Felix’s request is added, rideOS’ fleet optimization algorithm recalculates the optimal route and decides the next stop after the Chick-fil-A pickup will be to also pick up Felix’s order from Chipotle, then drop off Peter’s order and finally drop off Felix’s order.

# Checking on the state of the vehicle. There should now be 2 deliveries assigned to the vehicle.
# If you scroll through the plan of the vehicle, you'll notice that there are 2 trip ids that are assigned. # The plan will look like drive, pickup, drive, pickup, drive, dropoff, drive, dropoff.

GET_VEHICLE_STATE_URL = "https://api.rideos.ai/ride-hail-driver/v1/GetVehicleState"

get_vehicle_state_request = {
  "id": vehicle_id
}

response = requests.post(
  GET_VEHICLE_STATE_URL,
  headers=AUTHORIZATION_HEADER,
  json=get_vehicle_state_request
)

response.raise_for_status()
get_vehicle_state_response = response.json()
step_id = get_vehicle_state_response['state']['plan']['step'][0]['id']
trip_id = get_vehicle_state_response['state']['plan']['step'][0]['tripId']

print("Got successful get trip state response:")
print(json.dumps(get_vehicle_state_response, indent=4))

# Once the vehicle has arrived to a pickup/dropoff location, you can call the the CompleteSteps API
# to update the vehicle state

COMPLETE_STEP_URL = "https://api.rideos.ai/ride-hail-driver/v1/CompleteSteps"

complete_step_request = {
  "vehicle_id": vehicle_id,
  "trip_id": trip_id,
  "steps_to_complete": [{
    "step_id": step_id
  }]
}

response = requests.post(
  COMPLETE_STEP_URL,
  headers=AUTHORIZATION_HEADER,
  json=complete_step_request
)

response.raise_for_status()
complete_step_response = response.json()
print("Got successful complete step response:")
print(json.dumps(complete_step_response, indent=4))

# repeat the GetVehicleState and CompleteSteps call above whenever the vehicle completes a step

Conclusion

That's all there is to it! You have now successfully configured your fleet with a vehicle to handle on demand orders that contain multiple pickup and dropoff locations by leveraging rideOS’ RideHail API. The RideHail API automatically adjusts the optimal route based on incoming requests. Depending on your use case, if all the dropoffs are pre-scheduled (eg. not based on demand), you may want to do batch delivery like UPS to maximize overall fleet utilization. I’ll discuss how to achieve batch delivery in a future post.

I hope you find this tutorial helpful. You can find the complete code here. As we are at the beginning stages of this new capability, your feedback is appreciated. Please follow us on twitter and reach out to contact@rideos.ai if you have any questions!