Sandbox Coming Soon — We’re rolling out developer sandbox access. Contact us to join the early access waitlist.
This guide walks you through integrating the SmartMenu API into your ordering platform—from initial setup to personalized menu experiences.
Onboarding Checklist
Configure Your Chain
Work with EveryBite to set up your restaurant chain, locations, and menu sync.
Integrate the API
Follow the steps below to make your first API calls.
Test in Staging
Validate your integration against the staging environment with test data.
Go Live
Switch to production credentials and launch personalized menus to your guests.
Timeline
Phase Duration Activities Setup 1-2 days Credentials, chain configuration Development 1-2 weeks API integration, UI implementation Testing 3-5 days QA, edge cases, performance Launch 1 day Production deployment
What You’ll Need
A signed partnership agreement with EveryBite
API credentials for your authorized brand
Access to your ordering solution (currently Olo, with Toast, Square, and PAR coming soon)
Step 1: Get Your API Credentials
Once approved, you’ll receive API credentials that identify which restaurant brand’s menu data your app can access.
# Your API key will look like this:
API_KEY = "pk_YWJjMTIzLWRlZjQ1Ni03ODkw.x9Kj2mNpQrStUvWxYz"
Step 2: Start a Session
Start a session when the guest opens your app or begins browsing. Pass all the context upfront—chain, platform, guest identity—and we bind it to the session:
mutation StartSession {
startSession ( input : {
chainId : "honeygrow"
restaurantId : "loc_philly_walnut" # Optional: specific location
guestId : "loyalty_12345" # Your guest or loyalty ID
passportId : "passport_abc" # EveryBite Passport ID, if they have one
platform : IOS # IOS, ANDROID, WEB, KIOSK, POS, VOICE
appVersion : "3.2.1"
}) {
sessionId
chainId
restaurantId
}
}
{
"data" : {
"startSession" : {
"sessionId" : "sess_7f3a9c2e-8b1d-4e5f-a6c0-9d2e8f1a3b5c" ,
"chainId" : "honeygrow" ,
"restaurantId" : "loc_philly_walnut"
}
}
}
Store the returned sessionId — you’ll include it in all subsequent requests. All the context you passed (chain, platform, guest) is now bound to this session.
Once you have a session, configure your HTTP client with these headers for all API calls :
Header Required Description AuthorizationYes Your API key Content-TypeYes application/jsonX-Session-IDYes Session ID from Step 2
That’s it. All other context (chain, platform, guest identity) is already bound to your session from Step 2.
curl -X POST https://api.everybite.com/smartmenu/graphql \
-H "Content-Type: application/json" \
-H "Authorization: pk_YOUR_API_KEY" \
-H "X-Session-ID: sess_7f3a9c2e-8b1d-4e5f-a6c0-9d2e8f1a3b5c" \
-d '{"query": "..."}'
const headers = {
'Content-Type' : 'application/json' ,
'Authorization' : API_KEY ,
'X-Session-ID' : sessionId
};
Why so simple? You passed all the context (chain, platform, guest, passport) when you created the session. Now every API call just needs the session ID—we handle the rest.
Step 4: Fetch Filter Options
Before building your filter UI, fetch the available options for the restaurant:
query GetFilterOptions {
filterOptions {
diets { type displayName description isEnabled }
allergens { type displayName icon isEnabled }
nutrients { type displayName unit defaultMin defaultMax }
categories { name count }
}
}
{
"data" : {
"filterOptions" : {
"diets" : [
{ "type" : "VEGAN" , "displayName" : "Vegan" , "description" : "No animal products" , "isEnabled" : true },
{ "type" : "VEGETARIAN" , "displayName" : "Vegetarian" , "description" : "No meat or fish" , "isEnabled" : true },
{ "type" : "PESCATARIAN" , "displayName" : "Pescatarian" , "description" : "Fish but no meat" , "isEnabled" : true }
],
"allergens" : [
{ "type" : "DAIRY" , "displayName" : "Dairy" , "icon" : "dairy" , "isEnabled" : true },
{ "type" : "PEANUT" , "displayName" : "Peanut" , "icon" : "peanut" , "isEnabled" : true },
{ "type" : "TREE_NUT" , "displayName" : "Tree Nut" , "icon" : "tree-nut" , "isEnabled" : true }
],
"nutrients" : [
{ "type" : "CALORIES" , "displayName" : "Calories" , "unit" : "kcal" , "defaultMin" : 0 , "defaultMax" : 2000 },
{ "type" : "PROTEIN" , "displayName" : "Protein" , "unit" : "g" , "defaultMin" : 0 , "defaultMax" : 100 }
],
"categories" : [
{ "name" : "Salads" , "count" : 12 },
{ "name" : "Bowls" , "count" : 8 },
{ "name" : "Wraps" , "count" : 6 }
]
}
}
}
Step 5: Search Dishes with Preferences
Search for dishes that match the guest’s preferences:
query SearchDishes ( $preferences : PreferencesInput ) {
search ( preferences : $preferences ) {
matches {
dish {
id
name
description
nutrition { calories protein carbohydrates }
allergens { type displayName }
}
matchStatus
}
almostMatches {
dish {
id
name
nutrition { calories }
allergens { type displayName }
}
matchStatus
matchReasons
}
notMatches {
dish { id name }
matchStatus
matchReasons
}
counts { matches almostMatches notMatches total }
}
}
Variables:
{
"preferences" : {
"diets" : [ "VEGETARIAN" ],
"excludeAllergens" : [ "PEANUT" , "TREE_NUT" ],
"calorieRange" : { "max" : 600 }
}
}
{
"data" : {
"search" : {
"matches" : [
{
"dish" : {
"id" : "dish_001" ,
"name" : "Mediterranean Quinoa Bowl" ,
"description" : "Quinoa, roasted vegetables, feta, lemon tahini" ,
"nutrition" : { "calories" : 520 , "protein" : 18 , "carbohydrates" : 62 },
"allergens" : [{ "type" : "DAIRY" , "displayName" : "Dairy" }]
},
"matchStatus" : "MATCH"
}
],
"almostMatches" : [
{
"dish" : {
"id" : "dish_002" ,
"name" : "Garden Veggie Wrap" ,
"nutrition" : { "calories" : 480 },
"allergens" : [{ "type" : "SESAME" , "displayName" : "Sesame" }]
},
"matchStatus" : "ALMOST_MATCH" ,
"matchReasons" : [ "Contains Sesame (removable)" ]
}
],
"notMatches" : [
{
"dish" : { "id" : "dish_003" , "name" : "Thai Peanut Salad" },
"matchStatus" : "NOT_MATCH" ,
"matchReasons" : [ "Contains Peanut (excluded allergen)" ]
}
],
"counts" : { "matches" : 1 , "almostMatches" : 1 , "notMatches" : 1 , "total" : 3 }
}
}
}
Results are grouped by match status:
Status Meaning UI Recommendation MATCHFully meets preferences Show prominently ALMOST_MATCHMinor conflict (e.g., removable ingredient) Show with warning NOT_MATCHDoes not meet preferences Hide or show at bottom
Step 6: Display Results
Here’s how the response data translates to a user interface:
MATCH
Mediterranean Quinoa Bowl Quinoa, roasted vegetables, feta, lemon tahini 520 cal · 18g protein · 62g carbs Contains: Dairy ALMOST MATCH
Garden Veggie Wrap Grilled vegetables, hummus, mixed greens 480 cal · 14g protein · 58g carbs ⚠ Contains: Sesame (removable) NOT MATCH
Thai Peanut Salad Mixed greens, edamame, peanut dressing 410 cal · 12g protein · 38g carbs ✕ Contains: Peanut (excluded)
Sample Code
const { matches , almostMatches , notMatches , counts } = data . search ;
// Render MATCH dishes prominently
matches . forEach (({ dish , matchStatus }) => {
renderDishCard ( dish , { badge: 'green' , prominent: true });
});
// Render ALMOST_MATCH with warnings
almostMatches . forEach (({ dish , matchReasons }) => {
renderDishCard ( dish , { badge: 'yellow' , warnings: matchReasons });
});
// NOT_MATCH dishes: hide entirely or show at bottom
notMatches . forEach (({ dish , matchReasons }) => {
renderDishCard ( dish , { badge: 'red' , muted: true , reasons: matchReasons });
});
Get Early Access
Ready to integrate? We’re onboarding partners now.