Managing Store Menus
The Rappi API enables you to manage the menus of your stores.
You can create and update menus in the platform, retrieve the items of the menus, and review the approval status of your requests to create or update menus.
The main properties of the menus and the items created through the Rappi API consist of the following items:
storeId: The store identifieritems: The items in the menucategory: The category of the items in the menuid: The SKU (Stock-Keeping Unit) the ally gives to this categorymaxQty: The maximum number of items that can be ordered in this categoryminQty: The minimum quantity of elements that can be ordered in this category (In toppings, if it's 0 it means that it's not mandatory)name: Category namesortingPosition: If it's a product category, it's the position of the category in the menu. In case of being a Topping category, it's the position of the category within the product
children: Subitems nested in a categoryname: The name of the item in the menudescription: The description of the item in the menuimageUrl: The url of the image of the item in the menuprice: The price of the item in the menurappiIds: The identifier Rappi gives to this itemsku: The SKU (Stock-Keeping Unit) the ally gives to this itemsortingPosition: The position of the item in its categorytype: The type of the itemmaxLimit: Maximum indicator of the item, it's required only if the type is toppingcombo: Indicates if the element belongs to a combo. (if it is true it means that it belongs to a combo)
Important
When a menu is sent, full prices should be sent instead of prices with discounts applied
Some of these properties are subdivided into more objects. For a detailed view and a deeper explanation of these items, consult the Menus API resource on the Rappi API Reference.
Types of mapping
- No mapping: The ally loads their menu from the partners portal, without placing the SKUs of the menu items. Upon receiving an order, the json of the order will have the Rappi ids of the items but their SKUs will be null, so the ally is in charge of downloading their menu and encoding their SKUs.
- Self mapping: The ally loads their menu from the partners portal and can enter the SKU of the menu item from there. This sku information is taken to obtain the association between the SKU and the rappi id of the item, to send it as part of the order information to the ally's POS.
- Automatic mapping: The ally must send their menu consuming the POST menu endpoint, where SKUs must be received for each item that is sent in the menu. The mapping is generated by sending the json from the menu, generating a new mapping version that is available in such a way that when generating an order, the system takes the information registered in the automatic mapping database, and sends the information translated from the order to the ally's POS.
Improvements with automatic menu
We will reproduce the order of your productsin the Rappi app as you send it from the POS.We will keep the promotion settingswhen you update the menus from the POS.Menu validation will be immediate, avoiding any delay in loading, and you will receive feedback instantly ("accepted", "rejected", or with "error").We will keep the history of customersfavorites in the Rappi app, as we will keep your SKUs history.
Creating a Menu for a Store
Use the POST menu endpoint to create menus through API.
You can only send menus for parent stores. If you send a menu for a child store the endpoint will reject it.
To create a menu:
Make a POST request to the following URL, and add a JSON object to the body of the request with the following objects.
URL: https://{COUNTRY_DOMAIN}/api/v2/restaurants-integrations-public-api/menu
{COUNTRY_DOMAIN}: This is your Rappi Country Domain. See the list of Country Domains.
This is an example of the JSON object in the body of the request:
{ "storeId": "900103361", "items": [ { "name": "Grilled Chicken Burger", "description": "Grilled chicken burger description", "price": 14000, "sku": "10", "sortingPosition": 0, "type": "PRODUCT", "category": { "id": "2090019638", "maxQty": 0, "minQty": 0, "name": "Burgers", "sortingPosition": 0 }, "children": [ { "category": { "id": "211", "maxQty": 1, "minQty": 0, "name": "Do you want to add?", "sortingPosition": 0 }, "name": "French Fries", "description": "crunchy french fries", "price": 5000, "sku": "1", "maxLimit": 1, "sortingPosition": 1, "type": "TOPPING" }, { "category": { "id": "211", "maxQty": 1, "minQty": 0, "name": "Do you want to add?", "sortingPosition": 0 }, "name": "Potato Wedges", "price": 7000, "sku": "2", "maxLimit": 1, "sortingPosition": 1, "type": "TOPPING" } ] }, { "name": "Hawaiian Pizza", "description": "hawaiian pizza description", "price": 18000, "sku": "11", "sortingPosition": 1, "type": "PRODUCT", "category": { "id": "2090019639", "maxQty": 0, "minQty": 0, "name": "Pizzas", "sortingPosition": 1 }, "children": [] } ] }
Note
If then need to change product availability, can use availability module
Note
The values from this JSON are not real data. Ensure to replace them with your data when making API requests. You can add more items to the menu by adding more objects under items, separated by a comma.
The system retrieves the confirmation message Menu updated and ready to be validated.
Note
It's not possible to process more than 1 menu at a time, therefore, if there is already a menu in the approval process, all incoming menus from the same store will be ignored
Your menu is now under approval. You can consult the status of your menu by making a GET request to the menu endpoint. For more information, go to the Obtaining Menu Information section of this chapter.
Validations on the Received menu
When Rappi receives a menu through the POST menu endpoint, the catalog service runs a set of validations before accepting it. If any validation fails, the menu is rejected and the response includes the detail of each error.
The service applies a set of validations grouped into three families:
- Structural: shape of the menu and consistency between products, toppings and aisles.
- Content: quality and format of texts, images and prices.
- Limits: configurable maximum sizes.
How errors are reported
When a menu is rejected, the response is always HTTP 400. The body comes in one of two shapes, depending on which validation failed.
Simple format
When the service rejects the request early (for example: the same SKU used with different data, or a menu that is too deep), the body looks like:
{ "type": "<error_type>", "message": "..." }
Where <error_type> can be item_data_conflict, invalid_item_depth, among others.
Note
Even though some type values contain the word "conflict", the HTTP status is always 400 Bad Request, never 409.
Detailed format
When the full menu structure was evaluated, the body includes:
- A boolean for each validation (
trueif it passed,falseif it failed). item_level_depth: the depth of the validated path.path: the route inside the menu where the problem was detected (aisle → product → topping category → topping…).path_item_details: a list of details with the following format:
[Error]: <validation_name>, [Details]: <description>, [Location]: store_id=…, item_id=…, item_sku=…
Shortened response example:
{ "duplicated_items_complies": false, "item_level_depth": 3, "path": [ { "id": 50, "item_type": "CATEGORY", "sku": "burgers" }, { "id": 200, "item_type": "ITEM", "sku": "hamburger" } ], "path_item_details": [ "[Error]: duplicated_items_complies, [Details]: duplicated item in category, [Location]: store_id=123, category_id=50, item_id=200, item_sku=fries" ] }
Note
The technical names in parentheses (in snake_case) are the identifiers that appear literally in path_item_details. Error messages remain in English because that is how the service returns them.
Structural validations
-
Maximum menu depth (
invalid_item_depth): the menu supports up to 2 levels of hierarchy: a product at the first level and its toppings insidechildren. Nesting more children inside a topping is not allowed. The service rejects the request using the simple format with message:item with SKU '<sku>' exceeds maximum menu depth of 2 levels in menu integration V1. -
Consistent data for the same SKU (
item_data_conflict): if a SKU is repeated in different contexts of the menu, all of its attributes must match in every occurrence (name, description, price, image, quantities, list of children). If any differ, the menu is rejected using the simple format with a body of typeitem_data_conflict.For example, if you send the topping with SKU
topping-tomateat two different prices under the same topping category211, you receive:{ "type": "item_data_conflict", "message": "item with sku 'topping-tomate' has two different price values under topping category '211': $2500 and $3000" }Six kinds of divergence are detected. In every message,
<parentType>is one ofcorridor(aisle),product, ortopping category—the type of container that holds the conflicting item— and<parentSKU>is the identifier of that container:- Title:
item with sku '<sku>' has two different title values under <parentType> '<parentSKU>': '<t1>' and '<t2>'. - Description:
item with sku '<sku>' has two different description values under <parentType> '<parentSKU>': '<d1>' and '<d2>'. - Price:
item with sku '<sku>' has two different price values under <parentType> '<parentSKU>': $<p1> and $<p2>. - Image:
item with sku '<sku>' has two different image values under <parentType> '<parentSKU>': '<url1>' and '<url2>'. - Quantity (applies to topping categories —
maxQty,minQty, modifier type):item with sku '<sku>' has two different quantity values under <parentType> '<parentSKU>': min <m1>, max <M1>, type <t1> and min <m2>, max <M2>, type <t2>. - Children:
item with sku '<sku>' has conflicting child items under <parentType> '<parentSKU>'.
Note
If your menu comes from a POS where the same product or topping appears in multiple contexts (for example, the same "Tomato" topping is offered in several burgers), make sure the SKU is identical and that all attributes match in every occurrence. If you need a variation to have different attributes (for example, "Tomato" with a different price in another burger), give it its own SKU: each variation of the product must have a unique SKU.
- Title:
-
No duplicate aisles (
duplicated_categories_complies): inside the same menu, two aisles cannot exist with the sameidor the same SKU. Message:[Error]: duplicated_categories_complies, [Details]: duplicated category in menu, [Location]: …. -
No duplicate products or toppings (
duplicated_items_complies): inside an aisle, two products cannot have the sameidor SKU. The same applies inside a topping category for its toppings. Message:[Error]: duplicated_items_complies, [Details]: duplicated item in category, [Location]: …. -
Valid product ↔ topping category relation (
item_relation_complies): along any path inside the menu, two consecutive nodes of the same type are not allowed. A product must contain topping categories, and a topping category must contain toppings; never product → product nor topping category → topping category. Message:[Error]: item_relation_complies, [Details]: invalid relation between items and modifier groups found in path, [Location]: …. -
No cycles (
item_relation_without_cyclic_complies): the same SKU cannot appear twice in the same menu path, regardless of the element type. For example, if a product has SKUabc-123and somewhere inside its topping tree another element shows up with the same SKUabc-123(another product, a topping or a topping category), the validation fails. Message:[Error]: item_relation_without_cyclic_complies, [Details]: cyclic relation between items found in path, [Location]: item with id <n> and sku <sku> is duplicated in path. -
Path starts and ends with a product/topping (
start_and_ends_with_item): every menu path begins and ends with a product or topping, never with a topping category. Message:[Error]: start_and_ends_with_item, [Details]: path does not start and end with item, [Location]: …. -
At least one aisle (
minimum_categories_complies): the store must send at least one aisle. Message:[Error]: minimum_categories_complies, [Details]: no categories found in store menu, [Location]: store_id=…. -
At least one product (
minimum_items_complies): the store must send at least one product. Message:[Error]: minimum_items_complies, [Details]: no items found in store menu, [Location]: store_id=….
Content validations
-
Valid image URL (
image_url_complies): each image URL must be absolute (starting withhttp://orhttps://), contain no whitespace and be parseable. It is validated against the pattern^https?://[^\s]+(\?[^\s]*)?$. Message:[Error]: image_url_complies, [Details]: Invalid image URL <url>, Error: invalid image url format, [Location]: …. -
Text format (
text_format_complies): customer-visible texts (name, description, attributes, image paths) cannot contain emojis or blacklisted words. The SKU is only checked for emojis; blacklisted words inside a SKU produce a non-blocking warning (they do not reject the menu). The list of blacklisted words depends on the country (see below). Possible messages:[Error]: text_format_complies, [Details]: item title contains emoji characters: <text>, [Location]: …[Error]: text_format_complies, [Details]: item title '<text>' contains blacklisted words: '<word>', [Location]: …
-
Text field lengths (
text_fields_length_complies): allowed lengths:Field Minimum Maximum Product / topping SKU 1 500 Product / topping name 1 1000 Product / topping description 0 2000 Aisle / topping category SKU 0 500 Aisle / topping category name 0 1000 Aisle / topping category description 0 2000 Message:
[Error]: text_fields_length_complies, [Details]: item title length out of bounds: <n> not between [<min> and <max>], [Location]: …. -
Valid override types (
override_type_complies): an override can only be applied at certain levels depending on the field it overrides:Override Allowed levels Title STORE, ITEM Description STORE, ITEM Price STORE, ITEM Items STORE, ITEM Quantity STORE, ITEM Schedules STORE, ITEM, CATEGORY Suspension STORE Images STORE, ITEM Message:
[Error]: override_type_complies, [Details]: item <field> override has unsupported type: <type>, [Location]: ….If you send an override at a level that is not allowed, the service may reject it early using the simple format (
type: "item_data_conflict") with one of these messages:item with SKU '<sku>' and title '<t>' and type '<type>' is not permitted for an overrideitem with SKU '<sku>' and description '<d>' and type '<type>' is not permitted for an overrideitem with SKU '<sku>' and price '<p>' and type '<type>' is not permitted for an overrideitem with SKU '<sku>' and image '<img>' and type 'CATEGORY' is not permitted for an overrideitem with SKU '<sku>' and quantity is not permitted in override of type <type>
-
Non-negative numbers (
positive_numbers_complies): prices cannot be negative;min_permittedandmax_permittedcannot be negative; inside a topping categorymax_permittedmust be greater than 0. Messages:[Error]: positive_numbers_complies, [Details]: item has negative price: <p>, [Location]: …[Error]: positive_numbers_complies, [Details]: modifier_group item has zero max_permitted: 0, [Location]: …
-
Zero prices (
zero_price_complies): a product without toppings must have a price greater than 0. A product with toppings may have price 0 as long as at least one of its toppings has a price greater than 0. Message:Product price must be greater than 0 if the product doesn't have any children. Otherwise at least one of its children must have price.. -
Valid min_permitted (
min_permitted_complies): in standard products,min_permittedcannot be greater than 0. This restriction applies to the field on the product and on any override. Message:[Error]: min_permitted_complies, [Details]: item min_permitted greater than 0: <n>, [Location]: ….
Limit validations
- Max children per product / topping category (
max_item_childs_complies): maximum 50. Message:item with too many childs: <n> > 50. - Max images per product (
max_item_images_complies): maximum 3. Message:item with too many images: <n> > 3. - Max products per aisle (
max_items_category_complies): maximum 50. Message:category with too many items: <n> > 50. - Max time periods per schedule (
max_time_periods_complies): maximum 6. Message:schedule <id> with too many time periods: <n> > 6. - max_permitted within the parent's limits (
max_permitted_in_items_complies): a topping'smax_permittedmust respect themax_permitted(ormaxQty) of its containing topping category, and a topping category'smax_permittedcannot exceed that of its parent product. Message:[Error]: max_permitted_in_items_complies, [Details]: max_permitted exceeds parent max_permitted, [Location]: ….
Note
The numeric limits are configurable and may change over time. The values listed here are the ones in effect at the time this page was published.
Additional rules
On top of the validations above, the following rules are applied at the request level:
- Empty product list: the body must include at least one product. Error:
Items is required. - Valid item types: the first level must be
PRODUCTand the children must beTOPPING. Error:All parent items must be product type and children must be topping type. - Image file formats: referenced files must be PNG, JPEG, JFIF, JPG, WEBP, TIFF or BMP and weigh up to 1 MB.
Blacklisted words by country
The text_format_complies validation rejects texts that contain words from the country's blacklist. The list varies by country (AR, BR, CL, CO, CR, EC, MX, PE and UY each have their own list). The words fall into three categories:
- Competing platforms. Delivery and commerce brands whose names cannot appear in the menu content. Some are common to several countries (for example
uber,uber eats,ubereats,ifood,glovo,didi,pedidos ya,domicilios.com) and others are country-specific (BR:99 food,aiqfome,alfred delivery,delivery much,delivery center,james delivery,ze delivery,cornershop; CO/AR/CR:merqueo; and others). - Offensive or vulgar language. Insults, slurs, discriminatory language and profanity. The specific content varies by regional usage.
- Local political or topical references. Names of political figures, parties and movements in the country (varies by jurisdiction), as well as related terms such as
huelga,protestar,corrupto,fraude,manifestaciónand similar.
Note
The complete per-country list (word by word) changes often and is maintained on Rappi's side. If your pipeline needs the current list to filter content before sending the menu, contact the support team that supports your integration.
Recommendation: sanitize customer-visible content (name and description of products, toppings and aisles; attribute names) before sending it. If the content comes from the partner's POS or from a marketing team, avoid references to competitors, offensive language and political topics local to the store's country.
Note
Automatic menu does not support reusing skus (ids) when you create new topping categories, products, and toppings.
Obtaining Menu Information
After Rappi approves the content of the menu, you can retrieve the contents of your store menus using the GET menu endpoint.
To retrieve the contents of your store menus:
Make a GET request to the following URL.
URL: https://{COUNTRY_DOMAIN}/api/v2/restaurants-integrations-public-api/menu
{COUNTRY_DOMAIN}: This is your Rappi Country Domain. See the list of Country Domains.
The system retrieves a JSON object response with the information of your store menus.
Consulting the Approval Status of Your Menus
After creating a menu using our API, the Rappi team validates the structure and the contents of your menu.
You can use the GET menu/approved/{storeId} endpoint to consult the approval status of your menus.
To consult the status of your menus:
Make a GET request to the following URL.
URL: https://{COUNTRY_DOMAIN}/api/v2/restaurants-integrations-public-api/menu/approved/{storeId}
{COUNTRY_DOMAIN}: This is your Rappi Country Domain. See the list of Country Domains.
The system retrieves a JSON object response with the approval status of your store menus.
Note
If the wait time for a menu approval exceeds the agreed SLA, the support team must be contacted to validate the status of the menu.
Retrieving Your Last Created Menu
Use the GET menu/rappi/{storeId} endpoint to retrieve the information of the last menu created for a specific store.
To get the last created menu:
Make a GET request to the following URL:
URL: https://{COUNTRY_DOMAIN}/api/v2/restaurants-integrations-public-api/menu/rappi/{storeId}
-
{COUNTRY_DOMAIN}: This is your Rappi Country Domain. See the list of Country Domains. -
{storeId}: This is the identifier of your store integration.
The system retrieves a JSON response with the information of the last menu created for your store.
