Async order update

After a customer submits a food order, you can send an order update message to the Order with Google service to notify us of the change.

Here are some common reasons for sending order updates:

  • The estimated fulfillment time for the order becomes available or changes.
  • The state of an order changes.
  • The order can no longer be filled.
  • The price of a menu item included in the order changed.
  • The customer has a new way to manage their order, such as a customer support or restaurant phone number.
  • The receipt for the order becomes available.

The next sections provide details on how to address these different scenarios using order updates.

Transitioning order states

An order has six possible states. These states and their possible transitions are outlined in the following diagram:

When a customer first submits an order, the order begins with a state of CREATED, CONFIRMED, or REJECTED. You can send an order update message to update the state of an order, as long as the state transition is valid.

An order in the CONFIRMED state next moves to the IN_PREPARATION state. Depending on whether the order is for pickup or delivery, next use either the READY_FOR_PICKUP or IN_TRANSIT state. When the food has been delivered or picked up, the order is set to the FULFILLED state.

If you allow customers to cancel orders, you can use the CANCELLED state. An order can be cancelled while in the CREATED, CONFIRMED, IN_PREPARATION, READY_FOR_PICKUP, or IN_TRANSIT state. Your Order with Google service should issue refunds depending on your cancellation policy and the state of payments at the time of cancellation.

Your Order with Google service does not have to support all available states and transitions. However, the final state of the order must be FULFILLED, REJECTED, or CANCELLED.

Providing an estimated fulfillment time

You can provide users with an estimated time range for when their order will be ready for pickup (or delivered). Use the estimatedFulfillmentTimeIso8601 field of FoodOrderUpdateExtension to provide an estimated time range for when a customer's order will be ready for pickup or delivered.

Send the estimatedFulfillmentTimeIso8601 at the following times:

  • When the estimated time becomes available, ideally in the order CREATED or CONFIRMED state.
  • When the estimated time changes, such as updating the estimated time to be more accurate when the order is IN_TRANSIT.

To manage user expectations effectively, be conservative in your estimates and provide a date and time range rather than a fixed date and time. You should factor in variations such as traffic conditions whenever possible. For example, you can send an estimate of 12:45 PM (lower bound) to 1:15 PM (upper bound) for an order where the estimated delivery time is 1:00 PM.

Providing order management actions

When sending an order update, you can provide resources to customers that help them manage their order in the form of an OrderManagementAction. After a customer places an order, they may need to contact you or the restaurant fulfilling the order to track progress, make changes, or cancel their order.

An OrderManagementAction enables customers to email, call, or link to a URL directly from their device. Use the same information in OrderManagementAction as in the email order confirmation you send to the user.

Order management actions include the following types:

  • CUSTOMER_SERVICE: Provide customers with an action to contact customer service. This management action type is required for order updates.
  • EMAIL: Provide customers with an action to send an email to the provided email address.
  • CALL: Provide customers with an action to call the provided phone number.
  • VIEW_DETAIL: Provide customers with an action to view the details of their order.

Each order update must contain at least one order management action. However, the provided order management actions can vary based on the state of the order. For example, when an order is in the CONFIRMED state, the CUSTOMER_SERVICE action can point to your customer service phone number. When that order state updates to IN_TRANSIT, the CUSTOMER_SERVICE action can point to the fulfillment restaurant's phone number.

Sending order updates

You use the AsyncOrderUpdateRequestMessage message type to send an order update to the Order with Google service. Google responds back with an AsyncOrderUpdateResponseMessage. For example, if you wanted to inform a customer that their order was valid and accepted, you could send an AsyncOrderUpdateRequestMessage to change the state of the order to CONFIRMED with the label Accepted by restaurant.

Setting the order update message

When you send an AsyncOrderUpdateRequestMessage to Google, you must include information about the state of the order using the OrderUpdate field. The following examples show a sample AsyncOrderUpdateRequestMessage for each order state:

CONFIRMED

This example shows a sample order update request that notifies the user that the order is confirmed with a receipt and an estimated delivery time.

{
    "isInSandbox": true,
    "customPushMessage": {
        "orderUpdate": {
            "actionOrderId": "sample_action_order_id",
            "orderState": {
                "state": "CONFIRMED",
                "label": "Provider confirmed"
            },
            "receipt": {
                "userVisibleOrderId": "userVisibleId1234"
            },
            "updateTime": "2017-07-17T12:00:00Z",
            "orderManagementActions": [
                {
                "type": "CUSTOMER_SERVICE",
                "button": {
                    "title": "Contact customer service",
                    "openUrlAction": {
                        "url": "mailto:support@example.com"
                    }
                }
                },
                {
                "type": "EMAIL",
                "button": {
                    "title": "Email restaurant",
                    "openUrlAction": {
                        "url": "mailto:person@example.com"
                    }
                }
                },
                {
                "type": "CALL_RESTAURANT",
                "button": {
                    "title": "Call restaurant",
                    "openUrlAction": {
                        "url": "tel:+16505554679"
                    }
                }
                },
                {
                "type": "VIEW_DETAILS",
                "button": {
                    "title": "View order",
                    "openUrlAction": {
                        "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                    }
                }
                }
            ],
            "infoExtension": {
                "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
                "estimatedFulfillmentTimeIso8601": "2017-07-17T13:00:00Z/2017-07-17T13:30:00Z"
            }
        }
    }
}
    

REJECTED

This example shows a sample order update request that notifies the user that the order is rejected with a rejection reason.

{
    "isInSandbox": true,
    "customPushMessage": {
        "orderUpdate": {
            "actionOrderId": "sample_action_order_id",
            "orderState": {
                "state": "REJECTED",
                "label": "Order rejected"
            },
            "updateTime": "2017-05-10T02:30:00.000Z",
            "rejectionInfo": {
                "type": "UNKNOWN",
                "reason": "Sorry, the restaurant cannot take your order right now."
            },
            "orderManagementActions": [
                {
                    "type": "CUSTOMER_SERVICE",
                    "button": {
                        "title": "Contact customer service",
                        "openUrlAction": {
                            "url": "mailto:support@example.com"
                        }
                    }
                },
                {
                    "type": "EMAIL",
                    "button": {
                        "title": "Email restaurant",
                        "openUrlAction": {
                            "url": "mailto:person@example.com"
                        }
                    }
                },
                {
                    "type": "CALL_RESTAURANT",
                    "button": {
                        "title": "Call restaurant",
                        "openUrlAction": {
                            "url": "tel:+16505554679"
                        }
                    }
                }
            ],
            //[START food_orderupdate_extension]
            "infoExtension": {
            "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
            "foodOrderErrors": [
                {
                "error": "NO_CAPACITY",
                "description": "Sorry, the restaurant cannot take your order right now."
                }
            ]
            }
            //[END food_orderupdate_extension]
        }
    }
}
    

CANCELLED

This example shows a sample order update request that notifies the user that the order is cancelled with a cancellation reason.

{
    "isInSandbox": true,
    "customPushMessage": {
        "orderUpdate": {
            "actionOrderId": "sample_action_order_id",
            "orderState": {
                "state": "CANCELLED",
                "label": "Order cancelled"
            },
            "updateTime": "2017-05-10T02:30:00.000Z",
            "cancellationInfo": {
                "reason": "Customer requested"
            },
            "orderManagementActions": [
                {
                    "type": "CUSTOMER_SERVICE",
                    "button": {
                        "title": "Contact customer service",
                        "openUrlAction": {
                            "url": "mailto:support@example.com"
                        }
                    }
                },
                {
                    "type": "EMAIL",
                    "button": {
                        "title": "Email restaurant",
                        "openUrlAction": {
                            "url": "mailto:person@example.com"
                        }
                    }
                },
                {
                    "type": "CALL_RESTAURANT",
                    "button": {
                        "title": "Call restaurant",
                        "openUrlAction": {
                            "url": "tel:+16505554679"
                        }
                    }
                },
                {
                    "type": "VIEW_DETAILS",
                    "button": {
                        "title": "View order",
                        "openUrlAction": {
                            "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                        }
                    }
                }
            ]
        }
    }
}
    

IN_PREPARATION

This example shows a sample order update request that notifies the user that the food is currently being prepared.

{
    "isInSandbox": true,
    "customPushMessage": {
      "orderUpdate": {
        "actionOrderId": "sample_action_order_id",
        "orderState": {
          "state": "IN_PREPARATION",
          "label": "Order is being prepared"
        },
        "updateTime": "2018-04-15T11:30:00Z",
        "orderManagementActions": [
          {
            "type": "CUSTOMER_SERVICE",
            "button": {
              "title": "Contact customer service",
              "openUrlAction": {
                "url": "mailto:support@example.com"
              }
            }
          },
          {
            "type": "EMAIL",
            "button": {
              "title": "Email restaurant",
              "openUrlAction": {
                "url": "mailto:person@example.com"
              }
            }
          },
          {
            "type": "CALL_RESTAURANT",
            "button": {
              "title": "Call restaurant",
              "openUrlAction": {
                "url": "tel:+16505554679"
              }
            }
          },
          {
            "type": "VIEW_DETAILS",
            "button": {
              "title": "View order",
              "openUrlAction": {
                "url": "https://orderview.partner.com?orderid=sample_action_order_id"
              }
            }
          }
        ],
        "infoExtension": {
           "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
           "estimatedFulfillmentTimeIso8601": "PT20M"
        }
      }
    }
  },
  "json": true
}
}
    

READY_FOR_PICKUP

This example shows a sample order update request that notifies the user that the food is ready for pickup.

{
    "isInSandbox": true,
    "customPushMessage": {
        "orderUpdate": {
            "actionOrderId": "sample_action_order_id",
            "orderState": {
                "state": "READY_FOR_PICKUP",
                "label": "Order is ready for pickup"
            },
            "updateTime": "2018-04-15T12:00:00Z",
            "orderManagementActions": [
                {
                    "type": "CUSTOMER_SERVICE",
                    "button": {
                        "title": "Contact customer service",
                        "openUrlAction": {
                            "url": "mailto:support@example.com"
                        }
                    }
                },
                {
                    "type": "EMAIL",
                    "button": {
                        "title": "Email restaurant",
                        "openUrlAction": {
                            "url": "mailto:person@example.com"
                        }
                    }
                },
                {
                    "type": "CALL_RESTAURANT",
                    "button": {
                        "title": "Call restaurant",
                        "openUrlAction": {
                            "url": "tel:+16505554679"
                        }
                    }
                },
                {
                    "type": "VIEW_DETAILS",
                    "button": {
                        "title": "View order",
                        "openUrlAction": {
                            "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                        }
                    }
                }
            ],
            "infoExtension": {
                "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
                "estimatedFulfillmentTimeIso8601": "PT20M"
            }
        }
    }
}
    

IN_TRANSIT

This example shows a sample order update request that notifies the user that the order is in transit with an estimated delivery time.

{
    "isInSandbox": true,
    "customPushMessage": {
        "orderUpdate": {
            "actionOrderId": "sample_action_order_id",
            "orderState": {
                "state": "IN_TRANSIT",
                "label": "Order is on the way"
            },
            "inTransitInfo": {
                "updatedTime": "2017-07-17T12:00:00Z"
            },
            "updateTime": "2017-07-17T12:00:00Z",
            "orderManagementActions": [
                {
                    "type": "CUSTOMER_SERVICE",
                    "button": {
                        "title": "Contact customer service",
                        "openUrlAction": {
                            "url": "mailto:support@example.com"
                        }
                    }
                },
                {
                    "type": "EMAIL",
                    "button": {
                        "title": "Email restaurant",
                        "openUrlAction": {
                            "url": "mailto:person@example.com"
                        }
                    }
                },
                {
                    "type": "CALL_RESTAURANT",
                    "button": {
                        "title": "Call restaurant",
                        "openUrlAction": {
                            "url": "tel:+16505554679"
                        }
                    }
                },
                {
                    "type": "VIEW_DETAILS",
                    "button": {
                        "title": "View order",
                        "openUrlAction": {
                            "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                        }
                    }
                }
            ],
            "infoExtension": {
                "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
                "estimatedFulfillmentTimeIso8601": "PT20M"
            }
        }
    }
}
  

FULFILLED

This example shows a sample order update request that notifies the user that the order is picked up or delivered:

{
    "isInSandbox": true,
    "customPushMessage": {
        "orderUpdate": {
            "actionOrderId": "sample_action_order_id",
            "orderState": {
            "state": "FULFILLED",
            "label": "Order delivered"
            },
            "updateTime": "2017-05-10T02:30:00.000Z",
            "fulfillmentInfo": {
                "deliveryTime": "2017-05-10T02:30:00.000Z"
            },
            "orderManagementActions": [
                {
                    "type": "CUSTOMER_SERVICE",
                    "button": {
                        "title": "Contact customer service",
                        "openUrlAction": {
                            "url": "mailto:support@example.com"
                        }
                    }
                },
                {
                    "type": "EMAIL",
                    "button": {
                        "title": "Email restaurant",
                        "openUrlAction": {
                            "url": "mailto:person@example.com"
                        }
                    }
                },
                {
                    "type": "CALL_RESTAURANT",
                    "button": {
                        "title": "Call restaurant",
                        "openUrlAction": {
                            "url": "tel:+16505554679"
                        }
                    }
                },
                {
                    "type": "VIEW_DETAILS",
                    "button": {
                        "title": "View order",
                        "openUrlAction": {
                            "url": "https://orderview.partner.com?orderid=sample_action_order_id"
                        }
                    }
                }
            ]
        }
    }
}
    

Generate authorization token and send the message

Order updates require an authorization token so that the Order with Google service can verify the message is from your Order with Google web service.

To implement order updates for your project, follow these steps:

  1. Generate an authorization token by following these steps:
    1. Use the Google Auth Library to read the credentials from your service account file.
    2. Request token using the following API scope: https://www.googleapis.com/auth/actions.fulfillment.conversation
  2. Use this token to send an authenticated HTTP POST request to the following endpoint: https://actions.googleapis.com/v2/conversations:send
  3. Set the Content-Type header to application/json as part of your request.

The following examples demonstrate how to implement order updates:

Node.js

This code uses the Google auth library for Node.js.

const {auth} = require('google-auth-library')
const request = require('request');
// The service account client secret file downloaded from the Google Cloud Console
const serviceAccountJson = require('./service-account.json')
// order-update.json is a file that contains the payload
const jsonBody = require('./order-update.json')

/**
 * Get the authorization token using a service account.
 */
async function getAuthToken() {
  let client = auth.fromJSON(serviceAccountJson)
  client.scopes = ['https://www.googleapis.com/auth/actions.fulfillment.conversation']
  const tokens = await client.authorize()
  return tokens.access_token;
}

/**
 * Send an order update request
 */
async function sendOrderUpdate() {
  const token = await getAuthToken()
  request.post({
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    url: 'https://actions.googleapis.com/v2/conversations:send',
    body: jsonBody,
    json: true
  },
  (err, res, body) => {
    if (err) { return console.log(err); }
    console.log(`Response: ${JSON.stringify(res)}`)
  })
}
    

Python

This code uses the Google auth library for Python.

from google.oauth2 import service_account
from google.auth.transport.requests import AuthorizedSession
import json

# service-account.json is the service account client secret file downloaded from the
# Google Cloud Console
credentials = service_account.Credentials.from_service_account_file(
    'service-account.json')

scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/actions.fulfillment.conversation'])

authed_session = AuthorizedSession(scoped_credentials)

# order-update.json is a file that contains the payload
json_payload=json.load(open('order-update.json'))

response = authed_session.post(
    'https://actions.googleapis.com/v2/conversations:send',
    json=json_payload)
    

Java

This code uses the Google auth library for Java.

/**
 * Get the authorization token using a service account.
 */
private static String getAuthToken() {
  InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json");
  ServiceAccountCredentials.Builder credentialsSimpleBuilder =
      ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder();
  credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/actions.fulfillment.conversation"));
  AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken();
  return accessToken.getTokenValue();
}

/**
 * Send an order update request
 */
public void sendOrderUpdate() {
  String authToken = getAuthToken();
  // Execute POST request
  executePostRequest("https://actions.googleapis.com/v2/conversations:send",
      authToken, "update_order_example.json",);
}
    

For successful order updates with no errors, Google returns an HTTP 200 response with an empty payload. If there was an issue, such as the update being malformed, Google returns an error.