REST API for the Mbole Eats food delivery platform. All responses are JSON.

https://api.mboloeats.com
🔒 Endpoints marked Auth required expect a Firebase ID token as a Bearer token in the Authorization header: Authorization: Bearer <firebase-id-token>

Health

GET /api/health

Check API status and uptime.

Sample response
{
  "name": "Mbole Eats API",
  "status": "ok",
  "timestamp": "2026-05-25T10:00:00.000Z"
}

Auth

POST /api/auth/sync 🔒 Auth required

Sync the authenticated Firebase user with the database. Creates or updates the user record. Requires a valid Firebase ID token in the Authorization header.

Sample response
{
  "user": {
    "id": "uid_abc123",
    "name": "Amara Osei",
    "email": "amara@example.com",
    "phone": "+233201234567"
  },
  "message": "User data synchronized with Firebase Auth. This endpoint can be used to create or update user records in the database based on Firebase authentication."
}

Restaurants

GET /api/restaurants

List all restaurants. Optional query params: search (name), cuisine.

Sample response
{
  "count": 2,
  "restaurants": [
    {
      "id": "r_01",
      "name": "Chop Bar Central",
      "imageUrl": "https://cdn.mboloeats.com/restaurants/chop-bar.jpg",
      "cuisine": "Ghanaian",
      "rating": 4.5,
      "deliveryFee": 5,
      "deliveryTimeMinutes": 25,
      "isOpen": true,
      "address": "12 Independence Ave, Accra"
    },
    {
      "id": "r_02",
      "name": "Pizza Palace",
      "imageUrl": "https://cdn.mboloeats.com/restaurants/pizza-palace.jpg",
      "cuisine": "Italian",
      "rating": 4.2,
      "deliveryFee": 8,
      "deliveryTimeMinutes": 35,
      "isOpen": true,
      "address": "45 Ring Road, Accra"
    }
  ]
}
GET /api/restaurants/with-menus

List all restaurants including their full menu items.

Sample response
{
  "count": 1,
  "restaurants": [
    {
      "id": "r_01",
      "name": "Chop Bar Central",
      "imageUrl": "https://cdn.mboloeats.com/restaurants/chop-bar.jpg",
      "cuisine": "Ghanaian",
      "rating": 4.5,
      "deliveryFee": 5,
      "deliveryTimeMinutes": 25,
      "isOpen": true,
      "address": "12 Independence Ave, Accra",
      "menus": [
        {
          "id": "m_01",
          "restaurantId": "r_01",
          "name": "Waakye",
          "description": "Rice and beans with fish and shito",
          "imageUrl": "https://cdn.mboloeats.com/menu/waakye.jpg",
          "price": 25,
          "isAvailable": true
        }
      ]
    }
  ]
}
GET /api/restaurants/:restaurantId

Get a single restaurant by ID.

Sample response
{
  "restaurant": {
    "id": "r_01",
    "name": "Chop Bar Central",
    "imageUrl": "https://cdn.mboloeats.com/restaurants/chop-bar.jpg",
    "cuisine": "Ghanaian",
    "rating": 4.5,
    "deliveryFee": 5,
    "deliveryTimeMinutes": 25,
    "isOpen": true,
    "address": "12 Independence Ave, Accra"
  }
}
GET /api/restaurants/:restaurantId/menu

Get a restaurant together with all its menu items.

Sample response
{
  "restaurant": {
    "id": "r_01",
    "name": "Chop Bar Central",
    "cuisine": "Ghanaian",
    "rating": 4.5,
    "deliveryFee": 5,
    "deliveryTimeMinutes": 25,
    "isOpen": true,
    "address": "12 Independence Ave, Accra"
  },
  "count": 1,
  "menu": [
    {
      "id": "m_01",
      "restaurantId": "r_01",
      "name": "Waakye",
      "description": "Rice and beans with fish and shito",
      "imageUrl": "https://cdn.mboloeats.com/menu/waakye.jpg",
      "price": 25,
      "isAvailable": true
    }
  ]
}
POST /api/restaurants

Create a new restaurant or update an existing one (matched by name + cuisine). Optionally seed menu items in the same request.

Request body
{
  "restaurant": {
    "restaurantName": "Chop Bar Central",
    "cuisine": "Ghanaian",
    "rating": 4.5,
    "deliveryFee": 5,
    "deliveryTimeMinutes": 25,
    "isOpen": true,
    "address": "12 Independence Ave, Accra"
  },
  "menuItems": [
    {
      "name": "Waakye",
      "price": 25,
      "description": "Rice and beans"
    }
  ]
}
Sample response
{
  "restaurant": {
    "id": "r_01",
    "name": "Chop Bar Central",
    "imageUrl": null,
    "cuisine": "Ghanaian",
    "rating": 4.5,
    "deliveryFee": 5,
    "deliveryTimeMinutes": 25,
    "isOpen": true,
    "address": "12 Independence Ave, Accra"
  },
  "count": 1,
  "menu": [
    {
      "id": "m_01",
      "restaurantId": "r_01",
      "name": "Waakye",
      "description": "Rice and beans",
      "imageUrl": null,
      "price": 25,
      "isAvailable": true
    }
  ]
}

Menu Items

GET /api/menu

List menu items. Optional query param: restaurant_id.

Sample response
{
  "count": 2,
  "items": [
    {
      "id": "m_01",
      "restaurantId": "r_01",
      "name": "Waakye",
      "description": "Rice and beans with fish and shito",
      "imageUrl": "https://cdn.mboloeats.com/menu/waakye.jpg",
      "price": 25,
      "isAvailable": true
    },
    {
      "id": "m_02",
      "restaurantId": "r_01",
      "name": "Jollof Rice",
      "description": "Spiced tomato rice",
      "imageUrl": "https://cdn.mboloeats.com/menu/jollof.jpg",
      "price": 30,
      "isAvailable": true
    }
  ]
}
POST /api/menu

Add a single menu item to an existing restaurant.

Request body
{
  "restaurant_id": "r_01",
  "name": "Banku",
  "description": "Fermented corn dough with pepper",
  "price": 20,
  "isAvailable": true
}
Sample response
{
  "message": "Menu item created",
  "item": {
    "id": "m_03",
    "restaurantId": "r_01",
    "name": "Banku",
    "description": "Fermented corn dough with pepper",
    "imageUrl": null,
    "price": 20,
    "isAvailable": true
  }
}

Cart

GET /api/cart/active 🔒 Auth required

Get the active cart for the currently authenticated user.

Sample response
{
  "userId": "uid_abc123",
  "items": [
    {
      "menuItemId": "m_01",
      "restaurantId": "r_01",
      "quantity": 2,
      "name": "Waakye",
      "unitPrice": 25,
      "imageUrl": "https://cdn.mboloeats.com/menu/waakye.jpg",
      "subtotal": 50
    }
  ],
  "subtotal": 50
}
POST /api/cart 🔒 Auth required

Ensure a cart exists for the authenticated user and return it.

Sample response
{
  "message": "Cart ready",
  "cart": {
    "userId": "uid_abc123",
    "items": [],
    "subtotal": 0
  }
}
GET /api/cart/:userId 🔒 Auth required

Get a specific user's cart. Only the owner may access their cart.

Sample response
{
  "userId": "uid_abc123",
  "items": [
    {
      "menuItemId": "m_01",
      "restaurantId": "r_01",
      "quantity": 1,
      "name": "Waakye",
      "unitPrice": 25,
      "imageUrl": "https://cdn.mboloeats.com/menu/waakye.jpg",
      "subtotal": 25
    }
  ],
  "subtotal": 25
}
POST /api/cart/:userId/items 🔒 Auth required

Add a menu item to the cart. Quantity is incremented if the item already exists.

Request body
{
  "menuItemId": "m_01",
  "quantity": 1
}
Sample response
{
  "message": "Item added to cart",
  "cart": {
    "userId": "uid_abc123",
    "items": [
      {
        "menuItemId": "m_01",
        "restaurantId": "r_01",
        "quantity": 1,
        "name": "Waakye",
        "unitPrice": 25,
        "imageUrl": "https://cdn.mboloeats.com/menu/waakye.jpg",
        "subtotal": 25
      }
    ],
    "subtotal": 25
  }
}
PATCH /api/cart/:userId/items/:menuItemId 🔒 Auth required

Update the quantity of a specific cart item.

Request body
{
  "quantity": 3
}
Sample response
{
  "message": "Cart item updated",
  "cart": {
    "userId": "uid_abc123",
    "items": [
      {
        "menuItemId": "m_01",
        "restaurantId": "r_01",
        "quantity": 3,
        "name": "Waakye",
        "unitPrice": 25,
        "imageUrl": "https://cdn.mboloeats.com/menu/waakye.jpg",
        "subtotal": 75
      }
    ],
    "subtotal": 75
  }
}
DELETE /api/cart/:userId/items/:menuItemId 🔒 Auth required

Remove a specific item from the cart.

Sample response
{
  "message": "Item removed from cart",
  "cart": {
    "userId": "uid_abc123",
    "items": [],
    "subtotal": 0
  }
}
DELETE /api/cart/:userId 🔒 Auth required

Clear all items from the cart.

Sample response
{
  "message": "Cart cleared",
  "cart": {
    "userId": "uid_abc123",
    "items": [],
    "subtotal": 0
  }
}

Orders

POST /api/orders 🔒 Auth required

Place an order from the current cart. Cart is cleared after a successful order.

Request body
{
  "deliveryAddress": "12 Independence Ave, Accra",
  "paymentMethod": "cash_on_delivery"
}
Sample response
{
  "message": "Order created",
  "order": {
    "id": "o_xyz789",
    "userId": "uid_abc123",
    "items": [
      {
        "menuItemId": "m_01",
        "quantity": 2,
        "name": "Waakye",
        "unitPrice": 25,
        "subtotal": 50
      }
    ],
    "subtotal": 50,
    "deliveryFee": 20,
    "total": 70,
    "deliveryAddress": "12 Independence Ave, Accra",
    "paymentMethod": "cash_on_delivery",
    "status": "pending",
    "createdAt": "2026-05-25T10:05:00.000Z",
    "statusHistory": [
      {
        "status": "pending",
        "timestamp": "2026-05-25T10:05:00.000Z"
      }
    ]
  }
}
GET /api/orders/user/:userId 🔒 Auth required

Get all orders for the authenticated user.

Sample response
{
  "count": 1,
  "orders": [
    {
      "id": "o_xyz789",
      "created_at": "2026-05-25T10:05:00.000Z",
      "total": 70,
      "status": "pending",
      "totals": {
        "itemCount": 2,
        "cartTotal": 70
      },
      "items": [
        {
          "name": "Waakye",
          "qty": 2,
          "price": 25,
          "restaurantName": "Chop Bar Central"
        }
      ]
    }
  ]
}
GET /api/orders/all 🔒 Auth required

Get all orders across all users (admin use).

Sample response
{
  "count": 1,
  "orders": [
    {
      "orderId": "o_xyz789",
      "subtotal": 50,
      "deliveryFee": 20,
      "deliveryAddress": "12 Independence Ave, Accra",
      "paymentMethod": "cash_on_delivery",
      "status": "pending",
      "createdAt": "2026-05-25T10:05:00.000Z",
      "user": {
        "id": "uid_abc123",
        "name": "Amara Osei",
        "email": "amara@example.com",
        "phone": "+233201234567"
      }
    }
  ]
}
GET /api/orders/:orderId 🔒 Auth required

Get full details of a single order.

Sample response
{
  "order": {
    "id": "o_xyz789",
    "userId": "uid_abc123",
    "items": [
      {
        "menuItemId": "m_01",
        "quantity": 2,
        "name": "Waakye",
        "unitPrice": 25,
        "subtotal": 50
      }
    ],
    "subtotal": 50,
    "deliveryFee": 20,
    "total": 70,
    "deliveryAddress": "12 Independence Ave, Accra",
    "paymentMethod": "cash_on_delivery",
    "status": "confirmed",
    "createdAt": "2026-05-25T10:05:00.000Z",
    "statusHistory": [
      {
        "status": "pending",
        "timestamp": "2026-05-25T10:05:00.000Z"
      },
      {
        "status": "confirmed",
        "timestamp": "2026-05-25T10:07:00.000Z"
      }
    ]
  }
}
GET /api/orders/:orderId/restaurant-summary

Get an order summary formatted for the restaurant (includes customer info).

Sample response
{
  "order": {
    "orderId": "o_xyz789",
    "subtotal": 50,
    "deliveryFee": 20,
    "deliveryAddress": "12 Independence Ave, Accra",
    "paymentMethod": "cash_on_delivery",
    "user": {
      "id": "uid_abc123",
      "name": "Amara Osei",
      "email": "amara@example.com",
      "phone": "+233201234567"
    }
  }
}
PATCH /api/orders/:orderId/status

Update an order's status. Valid values: pending, confirmed, preparing, picked_up, on_the_way, delivered, cancelled.

Request body
{
  "status": "confirmed"
}
Sample response
{
  "message": "Order status updated",
  "order": {
    "id": "o_xyz789",
    "status": "confirmed",
    "statusHistory": [
      {
        "status": "pending",
        "timestamp": "2026-05-25T10:05:00.000Z"
      },
      {
        "status": "confirmed",
        "timestamp": "2026-05-25T10:07:00.000Z"
      }
    ]
  }
}

Delivery

GET /api/delivery/:orderId/tracking 🔒 Auth required

Get real-time tracking info for an order, including ETA and status history.

Sample response
{
  "orderId": "o_xyz789",
  "status": "on_the_way",
  "etaMinutes": 8,
  "history": [
    {
      "status": "pending",
      "timestamp": "2026-05-25T10:05:00.000Z"
    },
    {
      "status": "confirmed",
      "timestamp": "2026-05-25T10:07:00.000Z"
    },
    {
      "status": "preparing",
      "timestamp": "2026-05-25T10:10:00.000Z"
    },
    {
      "status": "picked_up",
      "timestamp": "2026-05-25T10:25:00.000Z"
    },
    {
      "status": "on_the_way",
      "timestamp": "2026-05-25T10:28:00.000Z"
    }
  ]
}

Likes

POST /api/likes

Like a restaurant on behalf of a user.

Request body
{
  "firebase_uid": "uid_abc123",
  "restaurant_id": "r_01"
}
Sample response
{
  "message": "Like saved",
  "like": {
    "firebaseUid": "uid_abc123",
    "restaurantId": "r_01",
    "createdAt": "2026-05-25T10:00:00.000Z"
  }
}
GET /api/likes/:firebase_uid

Get all restaurants liked by a user.

Sample response
{
  "count": 1,
  "likes": [
    {
      "firebaseUid": "uid_abc123",
      "restaurantId": "r_01",
      "restaurant": {
        "id": "r_01",
        "name": "Chop Bar Central",
        "imageUrl": "https://cdn.mboloeats.com/restaurants/chop-bar.jpg",
        "cuisine": "Ghanaian",
        "rating": 4.5
      },
      "likedAt": "2026-05-25T10:00:00.000Z"
    }
  ]
}
DELETE /api/likes/:firebase_uid/:restaurant_id

Remove a like from a restaurant.

Sample response
{
  "message": "Like removed",
  "like": {
    "firebaseUid": "uid_abc123",
    "restaurantId": "r_01"
  }
}