Empowering Restaurants through Integration

Follow

Creating Menus

A restaurant's menu of items is the basis for their business. Your POS integration will need to create their menu in the Grubhub system and sync it with their local changes.

To simplify this process, the Grubhub API now uses a single endpoint - POST /pos/v1/menu/ingestion - to create, modify, and remove items from a menu. This menu exists as a single object structured so it reflects how menus actually work in a restaurant:

  • Schedules exist at the top level of the menu object. These define what items are available during periods of the days and week. For example, you may have different items available during lunch hours or on weekends.

  • Each schedule contains a number of sections. These are larger groupings of menu items, like appetizers or entrees.

  • Naturally, each section contains several menu items. These are what diners actually order for delivery or pickup.

  • Each menu item can include a size prompt and one or more modifier prompts. Each of these can have several options. Sizes can affect the price of both the menu item itself and any modifiers. The modifiers can be optional or required, free or extra, and include things the type of salad dressing on a salad or add-ons like extra cheese.

In addition to these objects within the schedules object, a full menu has three other top-level objects:

  • info - Currently just contains a reference to the default tax rate.
  • tax_rates - The taxes that apply to this menu's items.

External IDs

Each of these objects must have an external_id associated with them. This is an identifying string that you assign to the object during creation. These strings can be anything - a number, a code linked to the POS, or arbitrary name.

During menu ingestions after the first, the API uses external IDs to match changed and removed items. It performs a diff on the current version and the uploaded version; any matching external IDs will have their fields updated, new IDs will create an object, and missing IDs will cause the associated item to be removed.

Example

Here's an example of the menu object sent to the ingestion endpoint:


{
  "tax_rates": [
    {
      "external_id": "79b5fc81",
      "name": "Default Tax Rate",
      "rate": 0.05,
      "included_in_item_price": true,
      "valid": true
    }
  ],
  "schedules": [
    {
      "external_id": "826991a0",
      "rules": [
        {
          "days": [
            "SUNDAY"
          ],
          "start": "07:00:00.000",
          "end": "19:00:00.000"
        },
        {
          "days": [
            "MONDAY"
          ],
          "start": "07:00:00.000",
          "end": "19:00:00.000"
        }
      ],
      "sections": [
        {
          "external_id": "79ffafb1",
          "name": "APPETIZERS",
          "description": "APPETIZERS",
          "items": [
            {
              "external_id": "79fb9101",
              "metadata": "metadata",
              "name": "Antipasto Misto",
              "description": "Brick oven-roasted peppers, zucchini, artichokes, olives, prosciutto, fresh asparagus, broccoli, fresh mozzarella and grape tomatoes served with baby field greens and balsamic vinaigrette. ",
              "calorie_content": "100",
              "minimum_serving_size": 0,
              "maximum_serving_size": 0,
              "minimum_order_quantity": 1,
              "media": "http://some.image.com/123",
              "tax_rate": "79b5fc81",
              "price": 399.0,
              "excludes_coupons": false,
              "size_prompt": {
                "external_id": "80747863",
                "name": "Choose a size",
                "sizes": [
                  {
                    "external_id": "21b83e1f",
                    "name": "Small",
                    "price": 2.99,
                    "calorie_content": "150"
                  },
                  {
                    "external_id": "fbd68d5f",
                    "name": "Large",
                    "price": 4.99,
                    "calorie_content": "250"
                  }
                ]
              },
              "deal": true,
              "tags": [
                {
                  "group": "LEGACY",
                  "name": "SODIUM_WARNING"
                }
              ],
              "modifier_categories": [
                {
                  "external_id": "79f416f0",
                  "name": "Antipasto Misto Remove Ingredients",
                  "min": 0,
                  "max": 8,
                  "included": 0,
                  "batch_quantity": 1,
                  "maximum_modifier_quantity": 1,
                  "price_adjustment": {
                    "type": "ADD",
                    "value": 0.5
                  },
                  "modifiers": [
                    {
                      "external_id": "79e23ca1",
                      "metadata": "metadata",
                      "name": "NO OLIVES",
                      "price": 0.0,
                      "calorie_content": "100",
                      "sized_prices": []
                    },
                    {
                      "external_id": "79e45f81",
                      "name": "NO DRESSING",
                      "description": "NO DRESS",
                      "price": 0.0,
                      "calorie_content": "100",
                      "sized_prices": []
                    }
                  ]
                }
              ]
            },
            {
              "external_id": "79fc5451",
              "name": "Mozzarella Fritta",
              "description": "Breaded mozzarella pan-fried in extra-virgin olive oil served over plum tomato sauce with fresh basil. \r\n",
              "calorie_content": "100",
              "minimum_serving_size": 0,
              "maximum_serving_size": 0,
              "minimum_order_quantity": 1,
              "tax_rate": "79b5fc81",
              "price": 799.0,
              "excludes_coupons": false,
              "coupons": [],
              "tags": [],
              "modifier_categories": []
            }
          ]
        },
        {
          "external_id": "7a1d22c0",
          "name": "SPECIALTIES",
          "items": [
            {
              "external_id": "7a16e131",
              "name": "Grilled Steak & Chicken Combo*",
              "description": "A combination of 6 oz. of sliced flat iron steak and a lemon-thyme chicken breast over tomatoes. Served with mashed potatoes and fresh asparagus. ",
              "calorie_content": "100",
              "minimum_serving_size": 0,
              "maximum_serving_size": 0,
              "minimum_order_quantity": 1,
              "tax_rate": "79b5fc81",
              "price": 1799.0,
              "excludes_coupons": false,
              "coupons": [],
              "tags": [],
              "modifier_categories": [
                {
                  "external_id": "7a142211",
                  "name": "Meat Temperature",
                  "min": 0,
                  "max": 5,
                  "included": 0,
                  "batch_quantity": 1,
                  "maximum_modifier_quantity": 1,
                  "modifiers": [
                    {
                      "external_id": "7a1337b1",
                      "name": "RARE",
                      "description": "**  RARE **",
                      "price": 0.0,
                      "calorie_content": "100",
                      "sized_prices": []
                    },
                    {
                      "external_id": "7a135ec1",
                      "name": "MEDIUM RARE",
                      "description": "**MED RARE**",
                      "price": 0.0,
                      "calorie_content": "100",
                      "sized_prices": []
                    },
                    {
                      "external_id": "7a13ace0",
                      "name": "MEDIUM",
                      "description": "** MEDIUM **",
                      "price": 0.0,
                      "calorie_content": "100",
                      "sized_prices": []
                    },
                    {
                      "external_id": "7a13d3f1",
                      "name": "MEDIUM WELL",
                      "description": "**MED WEL**",
                      "price": 0.0,
                      "calorie_content": "100",
                      "sized_prices": []
                    },
                    {
                      "external_id": "7a13fb01",
                      "name": "WELL DONE",
                      "description": "**WEL DONE**",
                      "price": 0.0,
                      "calorie_content": "100",
                      "sized_prices": []
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}