> ## Documentation Index
> Fetch the complete documentation index at: https://docs.everybite.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Integration Guide

> End-to-end guide for integrating SmartMenu API into your platform

This guide walks partners through integrating the SmartMenu API into their ordering platform.

## Prerequisites

Before starting integration:

1. **API Key**: Obtain from EveryBite partnership team
2. **Chain Setup**: Your restaurant chain(s) configured in EveryBite
3. **Data Sync**: Menu data synced between your ordering system and EveryBite
4. **Nutrition Data**: Restaurant has provided nutrition/allergen data

## Integration Checklist

### Phase 1: Setup

* [ ] Receive API key and staging environment access
* [ ] Confirm chain ID(s) for your restaurants
* [ ] Verify menu data sync is complete
* [ ] Test authentication with `filterOptions` query

### Phase 2: Session Tracking

* [ ] Implement `startSession` mutation when user begins ordering
* [ ] Pass a stable guest identifier (and Passport ID or email if applicable) to `startSession`
* [ ] Store returned `sessionId` and include in `X-Session-ID` header on all subsequent calls

### Phase 3: Core Functionality

* [ ] Implement search with `search` query
* [ ] Display match groups (matches, almost matches, not matches)
* [ ] Implement dish details with `dish` query
* [ ] Display nutrition panel and allergen badges

### Phase 4: Enhanced Features

* [ ] Implement BYO customization (if applicable)
* [ ] Real-time nutrition calculation
* [ ] Ingredient-level allergen warnings

### Phase 5: Go Live

* [ ] Switch to production API key
* [ ] Verify analytics events flowing to EveryBite
* [ ] Monitor error rates and latency
* [ ] Update privacy policy

***

## Implementation Patterns

### Pattern 1: Filter-First Experience

Best for apps where personalization is the primary feature.

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2563EB', 'primaryTextColor': '#fff', 'primaryBorderColor': '#1D4ED8', 'lineColor': '#64748b', 'secondaryColor': '#f1f5f9', 'tertiaryColor': '#e2e8f0'}}}%%
flowchart TD
    A["1. User opens menu"] --> B["Show preference selector"]
    B --> C["2. User sets preferences"]
    C --> D["search with preferences"]
    D --> E["3. Display results"]
    E --> F["Show Matches prominently"]
    E --> G["Show Almost Matches with warnings"]
    E --> H["Optionally show Not Matches greyed"]
    F --> I["4. User taps dish"]
    G --> I
    H --> I
    I --> J["dish query"]
    J --> K["Show full nutrition + allergen details"]

    classDef action fill:#2563EB,stroke:#1D4ED8,color:#fff,stroke-width:1px
    classDef result fill:#3B82F6,stroke:#2563EB,color:#fff,stroke-width:1px
    classDef display fill:#e2e8f0,stroke:#94a3b8,color:#334155,stroke-width:1px

    class A,C,I action
    class B,D,J result
    class E,F,G,H,K display
```

**Example Implementation:**

```typescript theme={null}
// 1. Start session when app loads (guest identity bound here)
useEffect(() => {
  const guestId = getGuestIdFromStorage() || generateGuestId();
  const passportId = getPassportIdFromStorage(); // null if guest

  const { data } = await graphqlClient.mutate({
    mutation: START_SESSION,
    variables: {
      input: {
        guestId,
        // OR passportId,
        // OR email: 'guest@example.com'
      }
    }
  });

  // Store session ID for all subsequent calls
  setSessionId(data.startSession.sessionId);
}, []);

// 2. Search with preferences (context comes from session)
async function searchDishes(preferences) {
  const { data } = await graphqlClient.query({
    query: SMART_MENU_SEARCH,
    variables: {
      preferences: {
        diets: preferences.diets,
        excludeAllergens: preferences.allergens,
        calorieRange: preferences.calorieRange
      }
    },
    context: {
      headers: {
        'X-Session-ID': sessionId
      }
    }
  });

  return data.search;
}
```

### Pattern 2: Overlay Enhancement

Best for apps with existing menu browsing where personalization is secondary.

```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2563EB', 'primaryTextColor': '#fff', 'primaryBorderColor': '#1D4ED8', 'lineColor': '#64748b', 'secondaryColor': '#f1f5f9', 'tertiaryColor': '#e2e8f0'}}}%%
flowchart TD
    A["1. User browses existing menu"] --> B["No SmartMenu call yet"]
    B --> C["2. User taps dish"]
    C --> D["dish query"]
    D --> E["3. Overlay nutrition badges"]
    E --> F["Vegetarian badge"]
    E --> G["Contains: Dairy, Tree Nut warning"]
    E --> H["Calories: 400 kcal"]

    classDef action fill:#2563EB,stroke:#1D4ED8,color:#fff,stroke-width:1px
    classDef result fill:#3B82F6,stroke:#2563EB,color:#fff,stroke-width:1px
    classDef display fill:#e2e8f0,stroke:#94a3b8,color:#334155,stroke-width:1px

    class A,C action
    class B,D result
    class E,F,G,H display
```

### Pattern 3: Real-Time BYO

For customizable dishes with live nutrition updates.

```typescript theme={null}
// 1. Load customization options
async function loadCustomizationOptions(dishId) {
  const { data } = await graphqlClient.query({
    query: DISH_CUSTOMIZATION_OPTIONS,
    variables: { dishId }
  });
  return data.dishCustomization;
}

// 2. Calculate nutrition when user changes selections
async function calculateNutrition(dishId, selections) {
  const { data } = await graphqlClient.mutate({
    mutation: CALCULATE_CUSTOM_NUTRITION,
    variables: {
      dishId,
      selections
    }
  });

  // Update UI with calculated nutrition
  // Show allergen warnings
  return data.calculateCustomNutrition;
}
```

***

## GraphQL Client Setup

### Apollo Client (React)

```typescript theme={null}
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

const httpLink = createHttpLink({
  uri: 'https://api.everybite-stage.com/smartmenu/graphql',
});

const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      'Authorization': process.env.SMARTMENU_API_KEY,
      'X-Session-ID': getSessionId(),
    }
  };
});

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
});
```

### Fetch (Vanilla JS)

```typescript theme={null}
async function smartMenuQuery(query, variables) {
  const response = await fetch('https://api.everybite-stage.com/smartmenu/graphql', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': API_KEY,
      'X-Session-ID': sessionId,
    },
    body: JSON.stringify({ query, variables })
  });

  const { data, errors } = await response.json();

  if (errors) {
    throw new Error(errors[0].message);
  }

  return data;
}
```

### Python (requests)

```python theme={null}
import requests

class SmartMenuClient:
    def __init__(self, api_key, session_id):
        self.endpoint = 'https://api.everybite-stage.com/smartmenu/graphql'
        self.headers = {
            'Content-Type': 'application/json',
            'Authorization': api_key,
            'X-Session-ID': session_id,
        }

    def query(self, query, variables=None):
        response = requests.post(
            self.endpoint,
            headers=self.headers,
            json={'query': query, 'variables': variables or {}}
        )
        result = response.json()

        if 'errors' in result:
            raise Exception(result['errors'][0]['message'])

        return result['data']
```

***

## Testing

### Staging Environment

Use staging environment for development and testing:

```
Endpoint: https://api.everybite-stage.com/smartmenu/graphql
```

### Test Scenarios

| Scenario                       | Expected Result                       |
| ------------------------------ | ------------------------------------- |
| Search with no preferences     | Returns all dishes ungrouped          |
| Search with Vegetarian         | Meat dishes in "Not Matches"          |
| Search with allergen exclusion | Dishes with allergen in "Not Matches" |
| View dish with allergens       | Allergen badges displayed             |
| BYO with allergen ingredient   | Warning displayed                     |
| Invalid session ID             | 400 error with clear message          |
| Missing required header        | 400 error specifying which header     |

### Sample Test Queries

<Info>
  Remember to include the `X-Session-ID` header with a valid session ID on all requests. See the [Session endpoint](/api/smartmenu/endpoints/session) for details on starting a session.
</Info>

```graphql theme={null}
# Test 1: Basic connectivity
query { __typename }

# Test 2: Filter options (context from X-Session-ID header)
query { filterOptions { diets { type } } }

# Test 3: Search (context from X-Session-ID header)
query {
  search {
    counts { total }
  }
}

# Test 4: Dish details (context from X-Session-ID header)
query { dish(id: "test-dish-id") { name, nutrition { calories } } }
```

***

## Go-Live Checklist

* [ ] Switch to production API key and endpoint
* [ ] Update base URL to `https://api.everybite.com/smartmenu/graphql`
* [ ] Verify all required headers are sent on every request
* [ ] Test error handling in production
* [ ] Monitor latency and error rates
* [ ] Confirm analytics events visible in EveryBite dashboard
* [ ] Update privacy policy to disclose data sharing with EveryBite

## Support

* **Technical Issues**: [api-partnerships@everybite.com](mailto:api-partnerships@everybite.com)
* **Partnership Questions**: [api-partnerships@everybite.com](mailto:api-partnerships@everybite.com)
* **Security Concerns**: [security@everybite.com](mailto:security@everybite.com)
