Skip to main content
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

1

Get API Credentials

Contact your EveryBite partnership manager to receive your staging and production API keys.
2

Configure Your Chain

Work with EveryBite to set up your restaurant chain, locations, and menu sync.
3

Integrate the API

Follow the steps below to make your first API calls.
4

Test in Staging

Validate your integration against the staging environment with test data.
5

Go Live

Switch to production credentials and launch personalized menus to your guests.

Timeline

PhaseDurationActivities
Setup1-2 daysCredentials, chain configuration
Development1-2 weeksAPI integration, UI implementation
Testing3-5 daysQA, edge cases, performance
Launch1 dayProduction 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.

Step 3: Configure Your Headers

Once you have a session, configure your HTTP client with these headers for all API calls:
HeaderRequiredDescription
AuthorizationYesYour API key
Content-TypeYesapplication/json
X-Session-IDYesSession 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:
StatusMeaningUI Recommendation
MATCHFully meets preferencesShow prominently
ALMOST_MATCHMinor conflict (e.g., removable ingredient)Show with warning
NOT_MATCHDoes not meet preferencesHide or show at bottom

Step 6: Display Results

Here’s how the response data translates to a user interface:

MATCH
Mediterranean Quinoa BowlQuinoa, roasted vegetables, feta, lemon tahini520 cal · 18g protein · 62g carbsContains: Dairy

ALMOST MATCH
Garden Veggie WrapGrilled vegetables, hummus, mixed greens480 cal · 14g protein · 58g carbs⚠ Contains: Sesame (removable)

NOT MATCH
Thai Peanut SaladMixed greens, edamame, peanut dressing410 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.