After a customer submits a food order, you can send an order update message to the Ordering End-to-End 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 fulfilled.
- 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. CREATED
state is used when the partner’s platform cannot confirm or reject the order
immediately. An example use case is when a customer orders through a delivery
aggregator. The delivery aggregator receives the delivery from Google, and the
aggregator sends the information to the restaurant. Once the restaurant received
and confirmed the order availability, state can now be CONFIRMED
, otherwise
REJECTED
.
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 Ordering End-to-End service should issue refunds depending on your
cancellation policy and the state of payments at the time of cancellation.
Your Ordering End-to-End 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
orCONFIRMED
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 Ordering End-to-End 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": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "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" } } }, { "type": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "infoExtension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension", "foodOrderErrors": [ { "error": "NO_CAPACITY", "description": "Sorry, the restaurant cannot take your order right now." } ] } } } }
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": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ] } } }
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" }, "receipt": { "userVisibleOrderId": "userVisibleId1234" }, "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": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "infoExtension":{ "@type":"type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension", "estimatedFulfillmentTimeIso8601":"PT20M" } } } }
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" }, "receipt": { "userVisibleOrderId": "userVisibleId1234" }, "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": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "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": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ], "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": "CALL_DRIVER", "button": { "title": "Call driver", "openUrlAction": { "url": "tel:+16505554681" } } } ] } } }
For more examples of order update requests in different use cases, read Implement advanced order updates.
Generate authorization token and send the message
Order updates require an authorization token so that the Ordering End-to-End service can verify the message is from your Ordering End-to-End web service.
To implement order updates for your project, follow these steps:
- Generate an authorization token by following these steps:
- Use the Google Auth Library to read the credentials from your service account file.
- Request token using the following API scope:
https://www.googleapis.com/auth/actions.fulfillment.conversation
- Use this token to send an authenticated HTTP POST request to the
following endpoint:
https://actions.googleapis.com/v2/conversations:send
- Set the
Content-Type
header toapplication/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.