Create Order Submission
For a given location, Grubhub passes parameters that identify which menu items and options a user would like to order (using the identifiers returned from the "Show Menu" call) along with:
- desired ready time in the timezone of the location (ISO8601)
- tip amount
- user details
- any stored metadata (which may include other login credentials if an account was created previously), and
- a discount amount (if applicable)
Grubhub expects a response (presented to the user) with the
- final subtotal
- applicable taxes, and
- soonest time at which the order could be available (in the local timezone of the location and ISO8601)
The order submission request payload closely resembles the order validation request payload. Grubhub only sends one order submission request, but may send multiple order validations. The order submission reflects the last corresponding order validation request sent. The order submission is an indication for the provider to send this order down to their Point-of-Sale and begin prepping the order for completion.
Request Endpoint
POST /locations/:provider_location_id/order_submissions
Example Request Body
{ "order_submission": { "desired_ready_time": "2016-03-11T11:44:08",
"force_prices": false, "fulfillment_type": "pickup",
"calculated_tax": 0, "grubhub_order_number": "430515324805810", "items": [ { "item": { "category_group_id": "6734384", "category_id": "11124", "name": "Turkey Sandwich", "price": 679, "provider_id": "1324", "quantity": 1, "recipient_name": "Joe Smith", "metadata": { "integration_id_needed_for_submission": "1234" }, "special_instructions": "Hold the mayo", "options": [ { "option": { "metadata": { "integration_id_needed_for_submission": "1111" }, "price": 100, "option_group_display_order": 4, "provider_id": "67478", "option_group_id": "565", "quantity": 2 } }, { "option": { "price": 0, "option_group_display_order": 2, "provider_id": "32791", "option_group_id": "577", "quantity": 1 } } ],
"tax_rate": "0.07" } }, { "item": { "category_group_id": "6734384", "category_id": "75759", "name": "Slice of cake", "price": 199, "provider_id": "9876", "quantity": 2, "recipient_name": "Jane Smith", "options": [ { "option": { "price": 0, "option_group_display_order": 1, "provider_id": "62459", "option_group_id": "123", "quantity": 1 } } ],
"tax_rate": "0.07" } } ], "location_time_zone": "America/New_York", "metadata": { "revel_authentication_token": "asdf232342453asdTSfs" }, "paid_via_ach": false,
"paid_via_ach": true,
"prepaid": true, "tip": 100, "merchant_funded_discount": 100, "special_instructions": "please double bag my order", "include_disposable_utensils": true, "user": { "born_at": "1986-03-11T11:44Z", "first_name": "joe", "gender": "male", "last_name": "smith", "email": "example@gmail.com", "password": "password123", "phone": "4445556666" }
"uuid": "4h3834000f1c0545c8d823da5a47e0b8" } }
Example Response Body
{ "order_submission": { "order_id": 15612, "tax": 123, "total": 978, "tip": 100, "provider_funded_discount": 100, "merchant_funded_discount": 100, "service_fee": 50, "expected_ready_at": "2016-03-11T11:44:08", "metadata": { "authentication_token": "asdf232342453asdTSfs" } } }
Example Request Body (Restaurant Self-Delivery)
{ "order_submission": { "delivery_fee": 300, "delivery_address": { "street_address": "One Federal Street", "extended_address": "Floor 6", "locality": "Boston", "region": "MA", "postal_code": "02110", "latitude": 42.356257, "longitude": -71.057225, "desired_ready_time": "2016-03-11T11:44:08",
"force_prices": false, "fulfillment_type": "delivery", "grubhub_order_number": "443213323803422",
"calculated_tax": 0, "items": [ { "item": { "category_group_id": "6734384", "category_id": "11124", "name": "Turkey Sandwich", "price": 878, "provider_id": "1324", "quantity": 1, "recipient_name": "Joe Smith", "options": [ { "option": { "metadata": { "integration_id_needed_for_submission": "1111" }, "price": 100, "option_group_display_order": 2, "provider_id": "67478", "option_group_id": "565", "quantity": 2 } } ],
"tax_rate": "0.07" } } ], "location_time_zone": "America/New_York", "merchant_funded_discount": 100, "metadata": { "revel_authentication_token": "asdf232342453asdTSfs" }, "paid_via_ach": false,
"paid_via_ach": true,
"prepaid": true, "special_instructions": "sauce on the side", "include_disposable_utensils": false, "tip": 100, "user": { "born_at": "1986-03-11T11:44Z", "first_name": "joe", "gender": "male", "last_name": "smith", "email": "example@gmail.com", "password": "password123", "phone": "4445556666" },
"uuid": "3bff033014c3013921b85e87feee5a7c" } }
Example Response Body (Restaurant Self-Delivery)
{ "order_submission": { "order_id": 15612, "delivery_fee": 300, "tax": 123, "total": 978, "tip": 100, "provider_funded_discount": 100, "merchant_funded_discount": 100, "service_fee": 0, "expected_ready_at": "2016-03-11T11:44:08", "metadata": { "authentication_token": "asdf232342453asdTSfs" } } }
Errors
If errors occur, Grubhub expects a response in the format:
{ "error": { "type": "provider", "message": "ITEM NOT AVAILABLE ERROR: Italian B.M.T.", "error_details": { "failed_items": [ { "provider_id": "/Cart/Product2.aspx?ID=30106&gID=3", "name": "Italian B.M.T." } ] } } }
Error Types
Site Errors: 422 HTTP status code
The underlying merchant ordering site renders an error that would have been displayed to the user to explain something like "Sorry, this location is closed right now" or "Oh no! We're out of guacamole". In this case, please return the actual error message the site renders to the user, an error code of 422.
Item Or Option Unavailable Errors: 422 HTTP status code
This is a subset of the case above, but should be handled with the following criteria in
mind:
- Please attempt to return an error message to us with the item/option name to us, rather than the ID, so that the user can amend their order appropriately. We will pass this message along to the user.
- Please also include extra attributes of failed_items and/or failed_options with an array of the item_ids and/or option_ids that are problematic. Problematic item_ids or option_ids might be items or options that are:
- Not currently available
- No longer on the menu
- Out of stock at the moment
- Simply unrecognized from your perspective
Integration Errors: 500 HTTP status code
If your service cannot interact with the merchant for some reason, please pass back a relevant message to help us understand what's going wrong along with an error code 500. Grubhub displays its own error message to the user.
This should include the scenario when the underlying merchant is non-responsive, i.e. perhaps their location ordering provider is down, in which case your integration lets Grubhub know so we can communicate to the user.
Parameter Errors: 500 HTTP status code
If we send you parameters that do not match your expectations and a message telling
us the error. This error will not be shown to the user, but may be used for informing us
we need to refresh our menu for the location. Send an error code of 500.
Menu Not found: 404 Error Code
If Grubhub attempts to issue a call and the merchant/location is no longer available from the provider (whether temporarily or permanently), subsequent requests from Grubhub for actions on that merchant should return a 404.
Service Down
If your service is down, Grubhub returns a generic error to the user, Error code 500.
Timeout Threshold
Order validation request expects a response within 25 seconds.
Payment
All Grubhub orders are prepaid and paid via ACH (Automated Clearing House). Grubhub will not send any payment information. Please consult with your Grubhub Account Advisor for information regarding payment reconciliation.
Amounts
All amounts are always in cents.
Handling Nested Options
Please note that Grubhub will return options in a "flattened" fashion in our ordering requests. This means that all options selected within an item will be returned on the same level, including the nested options. This will be returned by display order within the option group and display order within the option. The order of the options is similar to what is displayed and selected by the diner.
Grubhub returns the parent option group as option_group_id , which can help identify the nested option leveling.
Negative Options
Grubhub does not return any option groups or options that were NOT selected by the diner. We suggest adding a separate option group for negative options such, as "Remove Toppings".
Utensils Support
By default, all Grubhub orders have include_disposable_utensils set to "true". When a diner opts out of receiving utensils via the Grubhub Marketplace UI, the order payloads reflect this by sending include_disposable_utensils as "false" . This is sent on both the Order Validation and Order Submission request payload.
Ready Times
For the order_submission response, if the merchant doesn’t give an actual ready time for an order, please still include the expected_ready_at attribute, just have it be null. We only want it if an actual order ready time estimate is returned from the merchant. In the case Grubhub receives an expected_ready_at of nil we will generate our best estimation.
Special Instructions
Special_instructions on both the order and items are optional. If either is included in the post, they should be submitted along with the order. If they are omitted, then you don’t need to do anything relating to special instructions. Please also account for cases where “special_instructions”: null included is in the post.
Also note that, for delivery orders only, the delivery_address may include delivery_instructions for the driver such as Ring doorbell or Leave order on porch.
Order ID
This should be from the merchant (if available) to assist our users in retrieving their orders. String/integer are both accepted.
Handling Changes Between OV and OS
There are scenarios when certain attributes (special instructions on the order level, tips and desired_ready_time) may change between the OV and the OS. In this case, Grubhub will send order-scoped metadata to detail which attributes are changing. In this scenario, please update the relevant attributes on the providers cart before submitting the order.
In scenarios where the OV and OS are handled entirely separately (i.e. an OV redoes an OS) this should happen naturally.
In scenarios where you take advantage of a server-side cart and echo that token back to us, you either need to:
- Be able to update those three attributes without re-creating the entire cart.
- Recreate the cart entirely if those attributes do change between OV and OS.