API Basics
MENU APIs are organized around REST. The API has predictable resource-oriented endpoints, returns JSON-encoded responses, accepts JSON bodies for POST / PUT requests, and uses standard HTTP response codes, authentication and verbs.
Data Format
Resource IDs
All MENU resources come with a UUID as an indentifier, which is guaranteed to be unique for the resource type.
In cases where unique identifiers are generated by third-party systems (such as the POS system or a Loyalty platform), they are returned as generated by the third-party system, and may not follow a UUID format. Please review response parameter documentation of the API Reference for more details.
Notation
The snake_case notation is used for naming JSON resources and their attributes, for example serving_times, price_level.
Date & Time Format
All dates and timestamps are represented according to RFC-3339 standard with the extended notation (i.e. hyphen & dot separated). For example, 2020-11-05 is used for dates and 2020-11-05T18:35:52 for timestamps.
Timezones
All timestamps are always returned in UTC by the API and have to be converted by the API client. The Venue and Brand resources include a Timezone object, which can be used, in order to know into which timezone a timestamp needs to be converted to.
Amounts
All prices and order related charges are represented in minor currency units (i.e. cents) as integer values. For example, 0.35 USD will be represented as 35, 10 GBP as 1000. Currencies that have no minor currency units (i.e. currency exponent is 0), such as ISK or JPY, are also represented with a x100 factor applied, for example 10 JPY will be 1000.
Authentication
The MENU API supports two different types of authentication:
Application
The Application HTTP header identifies your application and is required for all API calls to MENU. Using only this authentication header you will be able to make all API requests that do not require a customer context, such as fetching details about a Venue (Store / Location):
Request
{
"method": "get",
"url": "https://api-public-playground.menu.app/api/venues/31f3769a-33ce-40d9-8f22-8d910db4fd11",
"headers": {
"X-Request-ID": "69da3547-204b-4093-a225-54e084c24215",
"Application": "f3a90488ffee32c3acb6fcd0ca417cf6",
"Api-Version": 4.38.0
}
}
You will receive your Application key as part of the integration kick-off process from your Integration Manager. Note that your Application key will be different per environment, such as from Playground to Production, but also between Production environments (e.g. US vs. EU), so make sure to set this up as a configurable variable.
Customer Account
For API calls that require a customer context, such as fetching all delivery addresses for a customer, additionally the Authorization HTTP header with a JWT token has to be provided:
Request
{
"method": "get",
"url": "https://api-public-playground.menu.app/api/customer-accounts/dacd5ded-ad3b-4155-89d2-0748b589ce71/delivery-addresses",
"headers": {
"X-Request-ID": "69da3547-204b-4093-a225-54e084c24215",
"Application": "f3a90488ffee32c3acb6fcd0ca417cf6",
"Api-Version": 4.38.0,
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwaS1wdWJsaWMtcGxheWdyb3VuZC5tZW51LmFwcC9hcGkvY3VzdG9tZXJzL3JlZnJlc2giLCJpYXQiOjE2OTEyMzk4MzQsImV4cCI6MTY5MTI0Mzc0NSwibmJmIjoxNjkxMjQwMTQ1LCJqdGkiOiJvSGw3aXVCanRLcjc1c0dTIiwic3ViIjoiNTA5MDYzNyIsInBydiI6ImNjMzI5MjFhMTU0ODBhMTE3ZDliYmM3MmMwZTEyNTZhNjg1MjQ1OGIiLCJhcHBsaWNhdGlvbl9pZCI6MjAxNSwic2Vzc2lvbl9pZCI6MjY2MTd9.5IqDOv5HHd1ddyojBMp63zVnl50VbbKEuhXFWbv25V8"
}
}
A JWT token can be obtained by going through the Sign In or Sign Up Flow, or by using Token Exchange (currently in development).
The Access Token is typically valid for 1 minute (60 seconds), while the Refresh Token is valid for 30 days (2628000 seconds). TTLs (time-to-live) are returned as part of the Token object.
API Requests & Responses
Requests
- Resources and Resource IDs are specified as part of the URL. Based on the hierarchy of resources, resources and resource IDs can be nested.
- For each request, the
Api-VersionHTTP header has to be specified - refer to Versioning for more information. - For each request, a unique
X-Request-IDHTTP header has to be specified. This ID is used for idempotency, as well as to allow for easy debugging. We recommend using a UUIDv4 generator to generate a unique Request ID.
For example to get all details of a Venue (Store / Location):
Request
{
"method": "get",
"url": "https://api-public-playground.menu.app/api/venues/31f3769a-33ce-40d9-8f22-8d910db4fd11",
"headers": {
"X-Request-ID": "69da3547-204b-4093-a225-54e084c24215",
"Application": "f3a90488ffee32c3acb6fcd0ca417cf6"
}
}
- For POST / PUT requests, JSON is accepted in the request body, for which the
Content-TypeHTTP header has to be set toapplication/json
For example to place an order:
Request
{
"method": "post",
"url": "https://api-public-playground.menu.app/api/orders",
"headers": {
"X-Request-ID": "69da3547-204b-4093-a225-54e084c24215",
"Application": "f3a90488ffee32c3acb6fcd0ca417cf6",
"Api-Version": 4.38.0,
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwaS1wdWJsaWMtcGxheWdyb3VuZC5tZW51LmFwcC9hcGkvY3VzdG9tZXJzL3JlZnJlc2giLCJpYXQiOjE2OTEyMzk4MzQsImV4cCI6MTY5MTI0Mzc0NSwibmJmIjoxNjkxMjQwMTQ1LCJqdGkiOiJvSGw3aXVCanRLcjc1c0dTIiwic3ViIjoiNTA5MDYzNyIsInBydiI6ImNjMzI5MjFhMTU0ODBhMTE3ZDliYmM3MmMwZTEyNTZhNjg1MjQ1OGIiLCJhcHBsaWNhdGlvbl9pZCI6MjAxNSwic2Vzc2lvbl9pZCI6MjY2MTd9.5IqDOv5HHd1ddyojBMp63zVnl50VbbKEuhXFWbv25V8",
"Content-Type": "application/json"
},
"body": {
"singular_point_id": "5af66188-5d95-4d2a-8508-ed47b4c19ea5",
"order_type": {
"id": 6,
"customer_phone_number": "+11234567",
"pickup_asap": true,
"pickup_at": "2022-09-29 18:00:00"
},
"menu_items" : [
{
"id": "19a39c00-3f23-11ed-936c-1a67b454859d",
"quantity": 1,
"price_level_id": "0ffb2a6a-3f23-11ed-936c-1a67b454859d",
"modifiers": [],
"comment": "Test comment from customer"
}
],
"payment_info": {
"payment_init_hash": "1c2dc93bc422ffbfb82c932f2d376dd7"
}
}
}
Responses
Responses are always JSON-encoded and have a predictable set of keys:
{
"code": 200,
"status": "OK",
"data": {
"*entity_name*": {
"*attribute_name*": "*attribute_value*"
}
}
}
For example, this is what getting details of a specific Venue (Store / Location) would look like:
Request
curl --request GET \
--url https://api-public-playground.menu.app/api/venues/31f3769a-33ce-40d9-8f22-8d910db4fd11 \
--header 'Application: f3a90488ffee32c3acb6fcd0ca417cf6' \
--header 'X-Request-ID: 69da3547-204b-4093-a225-54e084c24215'
Response
{
"status": "OK",
"code": 200,
"data": {
"venue": {
"id": "31f3769a-33ce-40d9-8f22-8d910db4fd11",
"uuid": "31f3769a-33ce-40d9-8f22-8d910db4fd11",
"name": "Kauwela Austin 1 Brink",
"code": "3181",
"currency_id": "dbf418a1-3ee5-4bad-b6a0-ff49f8db77e3",
"language_id": 1,
"external_dlc_id": null,
"brand_id": "de3a0527-7e4c-4888-91de-71b6d29efa74",
"timezone": {
"name": "America/Indiana/Indianapolis",
"offset": "-04:00"
},
"description": "",
"kiosk_receipt_footer": "",
"imprint": "",
"welcome_message": "",
"translations": {
"description": "",
"kiosk_receipt_footer": null,
"welcome_message": null
},
"address": "2111 Chicon St",
"state": 1,
"city": "Austin",
"zip": "78722",
"latitude": 30.2825159,
"longitude": -97.7242909,
"tax_number": "",
"phone": "723888237"
<!-- Shortened API response -->
}
}
}
API Endpoints
MENU maintains a number of different environments, including multiple production environments depending on your region. All data and authentication is unique to every environment, so make sure your application is built with this in mind, in order to be able to gracefully be promoted from one environment to another.
| Environment | API Endpoint |
|---|---|
| Staging | https://api-public-staging.menu.app |
| Playground | https://api-public-playground.menu.app |
| QA | https://api-public-qa.menu.app |
| Demo | https://api-public-demo.menu.app |
| US Production | https://api-public-us.menu.app |
| EU Production | https://api-public.menu.app |
| LAC Production | https://api-public-lac.menu.app |
| MENA Production | https://api-public-mena.menu.app |
| APAC Production | https://api-public-apac1.menu.app |
Localization & Translation
The MENU platform was architected with i18n as a core concept, and as such supports content in an unlimited number of languages, as configured by the brand in the CMS / Management Center.
In order for content to be returned in a specific language, you can specify the Content-Language HTTP Header in your API request. If content has been translated into that language through the CMS / Management Center, it will be returned in the specified language. Should the specified language not be available, the API will fallback to the default language that has been configured for the Brand.
When sending a Content-Language, translated content will be contained in a translations object within every resource that contains translated attributes:
"manual_location_inputs": [
{
"id": "464d8458-043d-4bdf-afae-ce01cd896a21",
"name": "San Francisco",
"translations": {
"name": "San Francisco"
}
}
]
Translations apply to menu, discount and manual locations related API calls.
Sorting
For many resources MENU allows for sorting, which allows brands to decide the order in which they want to present certain content (such as categories, subcategories and items) to their customers. Items in API responses are always returned already sorted and no custom sorting logic should be applied by the API client.
Versioning
The MENU API is versioned, in order to ensure backwards compability. With every API request, the Api-Version HTTP header has to be passed, which specifies the API Version that was used to build the integration.
The API Version may be used by MENU, in order to transform requests / responses, to ensure backwards compatibility. Breaking changes from one API version to another are documented in the API Changelog.
API Versions follow Release Versions documented in our Release Notes.
Backwards Compatibility
MENU ensures backwards compability for all request / response attributes that are documented. API endpoints may accept and / or return additional attributes that are not documented, though these should not be relied upon by your application, since they may change without notice. If you believe there is an additional request / response parameter that is essential to your implementation, please consult your Integration Manager on whether it is advisable to use, and if so we will adjust the API documentation.
In case MENU has to make a breaking change, which cannot be kept backwards compatible through API Versioning, you will receive a notice and due time to update your application.
Error Handling
The API signals errors via standard HTTP status codes. This means that successful responses use a 2xx status code, while errors are reported with 4xx or 5xx codes.
Standard HTTP Error Codes
| Status Code | Status Message | Description |
|---|---|---|
| 400 | Bad Request | Request could not be understood by the server due to malformed syntax |
| 401 | Unauthorized | Client must authenticate itself to get the requested response |
| 403 | Forbidden | User might not have necessary permissions for the resource, or may need an account |
| 404 | Not Found | Requested resource could not be found but may be available in the future |
| 405 | Method Not Allowed | Request method is not supported for the requested resource |
| 415 | Unsupported Media Type | Request entity has a media type which the server or the resource does not support |
| 422 | Validation Error | Some of the required fields are missing from the request body |
| 426 | Upgrade Required | Client must update its application to the newest one |
| 429 | Too Many Requests | Too many requests sent in a given amount of time |
| 500 | Internal Server Error | An unexpected condition was encountered and a specific message is not suitable |
Next to the HTTP code, the response body may contain additional details about the error, in order to help you debug:
{
"status": "Bad Request",
"code": 400,
"data": {
"info_message": {
"title": "Unauthorized access",
"body": "JWT token expired."
},
"message": "Bad Request"
}
}
Next Steps
Ready to make your first API calls? Then head on to First Steps