Implement last mile delivery
Learn how to implement a last mile delivery workflow. You can try out this tutorial in your development environment.
While this tutorial shows you how to create an order, the order you create is sent to a development version of the Fulfillment engine. You don't need to worry about groceries showing up at the example address we use!
To support your development efforts, this tutorial also includes tips and best practices to consider when you implement the workflow in a production environment.
- Before you begin
- Find store locations near the customer
- Create a Connect user account
- Fill a cart
- Understand changes required in the checkout process
- Reserve a time slot for delivery
- Create the order
- What happens to an order in a production environment?
- After an order is completed
- Summary
Before you begin
For this tutorial, you need the following items:
- A generated access token
- The base URL of your assigned Instacart development server
- Store locations are defined in your development environment
If store locations are not yet in your development environment, contact your Instacart representative.
Variables used in this tutorial
The following variables are used in requests in this tutorial. When you see a variable, substitute them with values that work in your development environment.
Variable | Description |
---|---|
<instacart_development_domain> | The domain of your assigned Instacart development server. |
<token> | The generated access token. |
<location_code> | After you find stores, select one to use in this tutorial. You'll need this value when you create an order. |
<service_option_id> | After you find available time slots, select one to use in this tutorial. You'll need this value when you reserve a time slot. |
<service_option_hold_id> | After you reserve the time slot, note the ID. You'll need this value when you create the order. |
The responses in this tutorial show sample values in place of the variables. The responses that are returned in your environment should have a similar format to the example responses. If you are interested in other possible responses, see the API documentation.
Find store locations near the customer
By default, if a store location is defined in your environment, a customer can request last mile delivery from it. Pickup only stores are omitted from the response.
Find the stores offering delivery near the specified address. In the request, substitute your values for the variables <instacart_development_domain>
and <token>
. If you don't have stores near this address, substitute an address that you know is close to one of your stores.
curl --request POST \
--url 'https://<instacart_development_domain>/v2/fulfillment/stores/last_mile' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"find_by": {
"address_line_1": "50 Beale St",
"postal_code": "94105"
}
}'
The response contains the array of store locations near the address.
{
"stores": [
{
"name": "Example Location 1",
"location_code": "123-4567",
"flags": {
"alcohol": true,
"pickup": false,
"pickup_only": false
}
},
{
"name": "Example Location 2",
"location_code": "123-4678",
"flags": {
"alcohol": true,
"pickup": false,
"pickup_only": false
}
},
{
"name": "Example Location 3",
"location_code": "123-4789",
"flags": {
"alcohol": false,
"pickup": false,
"pickup_only": false
}
}
],
"is_partial": false
}
From the response returned in your development environment, choose a store to use in this tutorial. You'll substitute the location code for the variable <location_code>
in requests.
When you get the list of stores, you can either choose a store location for the customer or allow the customer to select a store location where they want to shop. You can think of the selected store as the inventory store. If your retailer site passes this store location when it creates an order, the store location is also likely to be the fulfillment store.
Create a Connect user account
Create the Connect user account and validate the customer's address. The Connect user account contains the minimum information required by Instacart to create and fulfill an order. The account must have at least a unique user ID and the customer address. The user ID is defined by you, but it must be unique for all retailer customers. For example, you might choose user names or loyalty card IDs.
Create the user account and validate the address. In the request path, we create a user ID kamalsingh1234
. Substitute your values for the variables <instacart_development_domain>
and <token>
.
curl --request POST \
--url 'https://<instacart_development_domain>/v2/fulfillment/users/kamalsingh1234/addresses' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"address_line_1": "50 Beale St",
"postal_code": "94105",
}'
A successful JSON response has the status code 200
and the following fields:
{
"address": {
"address_line_1": "50 Beale St",
"postal_code": "94105"
},
"user_id": "kamalsingh1234"
}
If the user account already exists, for example if you or someone else has tried this tutorial on your development environment recently, the request returns the status code 400 User already created
. You can continue the tutorial with this ID or call the method again with a different user ID.
Save the generated user ID with your other customer account information and reuse it for all orders by that customer. It is up to you to ensure the user ID remains unique and associated with the correct customer. If the same ID is used for multiple customers, you risk introducing security and privacy concerns.
Fill a cart
To create an order, you need a cart with some items in it. Use your development environment to add an item to the cart that is available at the selected store.
Understand changes required in the checkout process
During the checkout process, your storefront needs to include prompts to collect the information necessary to create an order. For this tutorial, the Create the order request contains sample values for these items.
The information to collect before you create an order includes:
- A valid delivery address.
- Confirmation to leave the delivery unattended. Default is false.
- Confirmation if Electronic Benefits Transfer (EBT) is used as a payment method. Default is false.
- (Optional) Loyalty card number.
- (Optional) Special instructions.
- Tip amount.
- If a cart contains alcohol, the customer's date of birth. The customer must be old enough to legally purchase alcohol in their region. Advise the customer that they must show identification with their date of birth when the order is delivered and that alcohol cannot be left unattended.
- If you want to update your customer based on the callbacks your site receives about the status of the order, ask your customer to opt in to receiving SMS notifications.
Near the end of the checkout process, your storefront needs to handle the following tasks:
- Display the order details for review, and enable a customer to change items or update any of the collected information.
- Prompt for payment. Reserve the payment amount but wait to process the payment until after your site receives a callback to confirm that the order was delivered. Refunds might affect the total cost of the order.
- Prompt the user to select a time slot, as described in the next step.
Reserve a time slot for delivery
Before you can reserve a time slot, you need to retrieve a list of available time slots. After the customer selects a time slot, you can reserve a time slot for 10 minutes. If this is a time-sensitive last mile delivery order, see How to support time-sensitive last mile delivery.
Retrieve the list of available time slots. In the request, substitute your values for the variables <instacart_development_domain>
and <token>
.
curl --request POST \
--url 'https://<instacart_development_domain>/v2/fulfillment/users/kamalsingh1234/service_options/last_mile' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"address": {
"address_line_1": "50 Beale St",
"postal_code": "75041",
},
"cart_total_cents": 5400,
"items_count": 12
}'
The response contains a list of available time slots. Timestamps are returned in Coordinated Universal Time (UTC).
{
"service_options": [
{
"id": 3310259,
"date": "2021-06-22",
"window": {
"immediate_hour": 2
},
"availability": {
"available": true,
"reasons": [],
"item_codes": []
}
},
{
"id": 10358918,
"date": "2021-06-22",
"window": {
"immediate_hour": 5
},
"availability": {
"available": true,
"reasons": [],
"item_codes": []
}
},
{
"id": 439844911,
"date": "2021-06-22",
"window": {
"start_at": "2021-06-23T02:00:00Z",
"end_at": "2021-06-23T04:00:00Z"
},
"availability": {
"available": true,
"reasons": [],
"item_codes": []
}
},
...
]
}
From your response, choose a time slot to use in this tutorial. You'll substitute the ID for the variable <service_option_id>
in the next request.
Reserve the time slot. In the request, substitute your values for the variables <instacart_development_domain>
, <token>
, and <service_option_id>
.
curl --request POST \
--url 'https://<instacart_development_domain>/v2/fulfillment/users/kamalsingh1234/service_options/<service_option_id>/reserve' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json'
The response contains the details about the reserved time slot with the service option hold ID 357941827
.
{
"service_option_hold": {
"id": 357941827,
"expires_at": "2021-06-22T20:58:33Z",
"service_option": {
"id": 10358918,
"date": "2021-06-22",
"window": {
"immediate_hour": 5
},
"availability": {
"available": true,
"reasons": [],
"item_codes": []
}
}
}
}
From your response, note the service option hold ID. You'll substitute this value for the variable <service_option_hold_id>
when you create the order. The reservation is held for 10 minutes. To book the time slot, create the order.
- Convert the UTC timestamps to the customer's time zone before displaying the time slots.
- To avoid disappointing your customers, make the reservation as close to the end of the checkout process as possible. If a reservation expires before the order is created, Connect still attempts to book the time. Generally speaking, time slots have capacity for more than one order. If, however, the time slot capacity is fully booked, your storefront needs to prompt your customer to select a different time slot.
- If your retailer site enables customers to change a reserved delivery time slot, send the request again with the new time slot. Connect expires the previous time slot and uses the new one. A user account can have only one time slot reserved.
Create the order
Send a request to create an order as soon as possible after reserving the time slot. In the request, substitute your values for the variables <instacart_development_domain>
, <token>
, <order_id>
, <service_option_hold_id>
, and <location_code>
.
When you create a last mile delivery order, be sure to specify a unique, retailer-generated order_id
in the request. You’ll need to use this ID later to find and track the order in our system.
curl --request POST \
--url 'https://<instacart_development_domain>/v2/fulfillment/users/kamalsingh1234/orders/last_mile' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"order_id": "<order_id>",
"service_option_hold_id": "<service_option_hold_id>",
"location_code": "<location_code>",
"locale": "en-US",
"first_name": "Kamal",
"last_name": "Singh",
"user_phone": "800-555-0101",
"initial_tip_cents": 200,
"items_count": 12,
"bags_count": 3,
"items_weight": 15,
"cart_total": 5400,
"alcoholic": false,
"leave_unattended": true,
"special_instructions": "Ring the doorbell.",
"address": {
"address_line_1": "50 Beale St",
"postal_code": "94105"
}
}'
The response contains details about the newly created order.
{
"id": "<order_id>",
"status": "brand_new",
"order_url": "https://<instacart_development_domain>/s/dWl2N3U1d203",
"created_at": "2021-06-22T20:57:22Z",
"cancellation_reason": "",
"locale": "en_US",
"fulfillment_details": {
"store_location": "123-4789",
"window_starts_at": "2021-06-22T20:57:21Z",
"window_ends_at": "2021-06-23T01:57:21Z"
}
}
It can take several seconds to receive confirmation that the order is created. For your storefront, consider what you want to show to the customer while they wait for confirmation.
What happens to an order in a production environment?
As previously mentioned, an order created in a development environment is not fulfilled. To complete this tutorial, let's review what happens to orders created in a production environment.
In a production environment, a retailer employee receives the order. Based on the selected time slot, the employee shops for the order. Optionally, the retailer site can provide a way for the customer to update an order or cancel an order.
When the order is ready, the employee brings the bags to a staging area. Your retailer site calls the Stage an order endpoint, which updates the order status to staged
. The order is batched for delivery during the selected time slot. The assigned shopper picks up and delivers the order.
Instacart dispatches a shopper only after the order status is set to staged
. To ensure the delivery is made within the selected time slot, be sure to stage orders with enough lead time for a shopper to get to the store, pick up the order, and arrive at the customer's location.
You can get the order status with the Get an order endpoint.
After an order is completed
After an order is delivered to your customer, you can provide a way for your customer to update their tip and give feedback on the order. For more information about updating a tip, see Update a tip. For more information about giving order feedback, see Create or update order feedback (backend) and Create or update order feedback (frontend).
Summary
In this tutorial, we found stores near a customer that offered delivery, created a user account, and validated the address. Then we learned what information needs to be collected during checkout. We reserved a time slot and created an order. We discussed what happens in a production environment while the order is in the fulfillment process. Finally, we discussed providing a way for your customer to update their tip and give feedback on their completed order.