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. Provide a stable guest identifier and we bind it to a session:
mutation StartGuestSession {
  startSession(
    input: {
      guestId: "guest_abc123"
      # OR passportId: "passport_abc"
      # OR email: "guest@example.com"
    }
  ) {
    sessionId
  }
}
{
  "data": {
    "startSession": {
      "sessionId": "sess_7f3a9c2e-8b1d-4e5f-a6c0-9d2e8f1a3b5c"
    }
  }
}
Store the returned sessionId — you’ll include it in all subsequent requests via the X-Session-ID header. Guest identity is now bound to this session; chain context comes from your API key.

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. Chain context comes from your API key, and guest identity and personalization context come from the session you created in 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 provide guest identity once when you call startSession. After that, every API call just needs the X-Session-ID header—we resolve chain, widget, and personalization context for you.

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.

Join the Waitlist

Contact us for sandbox access

Architecture

Understand how the platform works

Core Concepts

Learn the data model

Authentication

API keys and security