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,
      "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,
      "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,
      "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,
    "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,
    "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,
    "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,
    "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. deliveryFee is calculated automatically from the distance between each restaurant's location and deliveryAddress, at 500 francs per mile (using the farthest restaurant when the cart spans multiple restaurants).

Request body
{
  "deliveryAddress": {
    "latitude": 5.6037,
    "longitude": -0.187
  },
  "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": 1450,
    "total": 1500,
    "deliveryAddress": {
      "latitude": 5.6037,
      "longitude": -0.187
    },
    "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"
  }
}