In your order taking implementation, you will have have to programmatically answer some common questions. We’ve built our API to quickly answer these questions, as well as handle edge cases; however, you may not have time to sort through each and every field in a request’s response payload.
In this topic, we’ve gathered some of the more common situations you’ll need to handle and provided pointers to the API endpoints and fields that help you solve them. If you have a question that hasn’t been answered here, please let your account representative know.
Is this restaurant open?
You can retrieve this information from GET restaurants/availability_summaries?ids={restaurant_id}. For reference, here’s what that response looks like:
{
"availability_summaries": [
{
"restaurant_id": "11402556",
"open": true,
"available_for_delivery": true,
"available_for_pickup": true,
"delivery_offered_to_diner_location": false,
"restaurant_name": "Design Kitchen Too",
"address": {
"locality": "Holland",
"region": "MI",
"postal_code": "49423",
"street_address": "10 E 10th St",
"country": "USA"
},
"decimal_distance_in_miles": null,
"logo": "https://dtyxqspugqu5z.cloudfront.net/logo/2556/11402556/20170301BackgroudTileopt1.png",
"cuisines": [],
"delivery_estimate": 30,
"pickup_estimate": 15,
"order_minimum": {
"amount": 500,
"currency": "USD"
},
"delivery_fee": {
"amount": 0,
"currency": "USD"
},
"delivery_cutoff": 15,
"pickup_cutoff": 30,
"cutoff_for_delivery": false,
"cutoff_for_pickup": false
}
]
}
Checking whether a restaurant is open is not entirely simple. Your first check should be to see if availability_summaries.open=true. That means the current time is within the restaurant’s stated hours of operation.
In most cases, this is enough. However, there are edge cases where a restaurant would be open and not available. Depending on the fulfillment method, you should check that either availability_summaries.cutoff_for_delivery=false or cutoff_for_pickup=false; that is, the diner is not ordering in the half hour before close where the restaurant stops accepting delivery orders.
You should also check these when the diner selects an individual restaurant and you call GET /restaurants/11402556?hideUnavailableMenuItems=true. This call’s response payload, in the restaurant_availability object, will include two other fields worth checking: is_blacked_out and is_white_in. A blacked out restaurant is temporarily refusing orders, while a whited in one takes orders when it’s listed as closed.
We’re seen some diners take their time putting together an order, so our carting APIs automatically double-check that the restaurant is still open. When you call GET /carts/{cart_id}/bill, look for error messages in the validation_errors field.
Does the restaurant deliver to the diner?
This is a separate question from whether the restaurant is open; it addresses whether the diner is within the restaurant’s delivery area. For the API to provide this answer, you must have attached delivery information to the diner’s cart.
From the availability summaries response above, check to see if the available_for_delivery and delivery_offered_to_diner_location fields are both true. The first confirms that the restaurant is currently delivering orders, while the second confirms that the diner is within their delivery area.
Is this menu item available to order?
Before adding a menu item to a cart, you may want to check whether a menu item is currently available to order. Menu items may be unavailable because of menu schedules (e.g. breakfast items unavailable after 11 am) or because the restaurant has made them temporarily unavailable (that is, 86-ed them).
Whether you retrieve menu information for the entire restaurant from GET /restaurants/{restaurant_id}?hideMenuItems=false or for a single item from GET /restaurants/{restaurant_id}/menu_items/{menu_item_id}, the response will include one or more menu item objects:
{
"id": "15609443",
"menu_category_name": "Sandwiches",
"name": "Jumbo Dog",
"description": "",
"price": {
"amount": 299,
"currency": "USD"
},
"tax": 10,
"minimum_cart_total": {
"amount": 0,
"currency": "USD"
},
"sequence": 2,
"minimum_price_variation": {
"amount": 299,
"currency": "USD"
},
"maximum_price_variation": {
"amount": 299,
"currency": "USD"
},
"available": true,
"choice_category_list": [
{
"id": "5487570",
"name": "Choice of Sides ",
"min_choice_options": 2,
"max_choice_options": 2,
"choice_option_list": [
{
"id": "21177018",
"description": "French Fries",
"price": {
"amount": 0,
"currency": "USD"
},
"choice_category_list": [],
"price_changes": {},
"sequence": 1
},
{
"id": "21177019",
"description": "Sweet Potato Fries",
"price": {
"amount": 0,
"currency": "USD"
},
"choice_category_list": [],
"price_changes": {},
"sequence": 2
},
{
"id": "21177020",
"description": "Baked Potato",
"price": {
"amount": 0,
"currency": "USD"
},
"choice_category_list": [],
"price_changes": {},
"sequence": 3
},
{
"id": "21177021",
"description": "Mashed Potatoes",
"price": {
"amount": 0,
"currency": "USD"
},
"choice_category_list": [],
"price_changes": {},
"sequence": 4
},
{
"id": "21177022",
"description": "Green Beans",
"price": {
"amount": 0,
"currency": "USD"
},
"choice_category_list": [],
"price_changes": {},
"sequence": 5
},
{
"id": "21177023",
"description": "Carrots",
"price": {
"amount": 0,
"currency": "USD"
},
"choice_category_list": [],
"price_changes": {},
"sequence": 6
},
{
"id": "21177024",
"description": "Corn",
"price": {
"amount": 0,
"currency": "USD"
},
"choice_category_list": [],
"price_changes": {},
"sequence": 7
},
{
"id": "21177025",
"description": "Peas",
"price": {
"amount": 0,
"currency": "USD"
},
"choice_category_list": [],
"price_changes": {},
"sequence": 8
}
],
"variation_target": false,
"sequence": 15
}
],
}
To verify that a menu item is available, check if the available field is true.
As a backup precaution, the carting API checks to make sure any item in the cart at checkout is still available to order.
Does the diner already have a cart in progress?
When you call POST /carts, the Grubhub API will create a new, empty cart for the current diner. To find if a diner has any existing, in-progress carts, call GET /carts. Here’s what that looks like with two in-progress carts:
{
"carts": {
"x1nBUbnKEee63MGdrAMb3A": {
"id": "x1nBUbnKEee63MGdrAMb3A",
"diner_id": "mBVZAH0kEee5ZUE5jKZbTA",
"currency": "USD",
"fulfillment_info": null,
"notification_preferences": null,
"charges": null,
"payments": null,
"restaurant_ids": [],
"restaurants": [],
"time_placed": null,
"recommended_tip_settings": {
"minimum_tip_amount": 0,
"minimum_tip_percentage": 0
},
"state": "ACTIVE",
"status": "ACTIVE",
"asap": true
},
"wnOioLnKEeesF4eJYIxerA": {
"id": "wnOioLnKEeesF4eJYIxerA",
"diner_id": "mBVZAH0kEee5ZUE5jKZbTA",
"currency": "USD",
"fulfillment_info": null,
"notification_preferences": null,
"charges": null,
"payments": null,
"restaurant_ids": [],
"restaurants": [],
"time_placed": null,
"recommended_tip_settings": {
"minimum_tip_amount": 0,
"minimum_tip_percentage": 0
},
"state": "ACTIVE",
"status": "ACTIVE",
"asap": true
}
}
}
A diner should not have multiple carts active; this likely points to a bug in your implementation code.
Is a cart ready for checkout?
For a cart to successfully checkout, it needs the following:
- At least one item added to cart.
- Delivery or pickup information.
- Payment details.
When you call GET /carts/{cart_id}/bill, you will receive a checkout token if the cart is ready to go. If not, you can check the validation_errors object to see which property caused the error.
Here’s what this field looks like for a newly-created cart sent to the bill endpoint:
"validation_errors": [
{
"property": "fulfillment_info",
"message_key": "invalid-fulfillmentinforequired"
},
{
"property": "payments",
"message_key": "invalid-required"
},
{
"property": "lines",
"message_key": "invalid-required"
}
]
In this way, you can use the bill endpoint to check if a cart is ready to send to the restaurant. Additionally, the bill endpoint will verify that the restaurant is still open and the menu item is still available to order.
How do I handle cash payments?
Cash payments use only the last step in the payment process. Because payment occurs at the same time as when the diner receives their food, you only need to attach the payment to the cart, like this:
POST /carts/CwNJoY8rEee3QaeudTGoRQ/payments HTTP/1.1
Host: api-gtm.grubhub.com
Authorization: Bearer xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx
Content-Type: application/json
{
"type": "CASH"
}
We do perform a fraud check to make sure that this payment is actually likely to occur. If this request fails, you'll receive a 422 error code and the following JSON object:
{
"property":"payment_type",
"message_key":"invalid-notaccepted"
}
Is a payment method valid?
Because you need to use Braintree Payments to manage credit cards, you will also use their system to verify whether a payment method is valid.
They offer two ways to verify payments:
- Call a single use API:
options().verifyCard(). - Enable it in your Control Panel.
Consult their documentation for full details.