NearbyMe Food Delivery API

Version 1.0.0 | Base URL: /api

Overview
Complete REST API documentation for the NearbyMe food delivery marketplace platform.

This documentation covers all REST API endpoints for the NearbyMe food delivery platform. Use the navigation on the left to browse by category or scroll through all endpoints.

Authentication
## Authentication NearbyMe uses Supabase Auth for authentication. All authenticated endpoints require a valid JWT token in the Authorization header. ### Getting Started 1. **Sign Up**: Create an account at `/auth/sign-up` 2. **Sign In**: Authenticate at `/auth/login` 3. **Get Session**: After authentication, Supabase provides a session with access token ### Using Authentication in API Calls For all authenticated endpoints, include the Authorization header: ``` Authorization: Bearer YOUR_ACCESS_TOKEN ``` **Note**: When testing via browser (same origin), the session cookie is automatically sent. For Postman or external testing, you need to include the token manually. ### Roles - **customer**: Can browse restaurants, manage cart, place orders - **vendor**: Can manage restaurant, menu, view/process orders - **rider**: Can view available deliveries, update delivery status - **admin**: Full system access
Test Data Reference
Use these pre-seeded values for testing:

Test Kitchen Lagos

a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d
End-to-End User Flows
Complete workflows for each user role from onboarding to core actions

Customer Flow: Browse, Order, Track

1Sign Up

Create a customer account

Navigate to /auth/sign-up and select "Customer" role

2Browse Restaurants

View available restaurants

GET /api/restaurants
curl -X GET "/api/restaurants?latitude=6.5244&longitude=3.3792"
3View Menu

Get menu items for a restaurant

GET /api/restaurants/{id}/menu-items
curl -X GET "/api/restaurants/a1b2c3d4-e5f6-4a5b-8c9d-0e1f2a3b4c5d/menu-items"
4Add to Cart

Add items to shopping cart

POST /api/cart
curl -X POST "/api/cart" -H "Authorization: Bearer TOKEN" -d '{"menu_item_id": "aaaa1111-1111-1111-1111-111111111111", "quantity": 2}'
5Apply Promo (Optional)

Apply a discount code

POST /api/cart/promo
curl -X POST "/api/cart/promo" -H "Authorization: Bearer TOKEN" -d '{"promo_code": "WELCOME20"}'
6Checkout

Create order from cart

POST /api/orders
curl -X POST "/api/orders" -H "Authorization: Bearer TOKEN" -d '{"delivery_address": "45 Admiralty Way, Lagos", "delivery_latitude": 6.4355, "delivery_longitude": 3.4500}'
7Track Order

Monitor order status

GET /api/orders/{id}
curl -X GET "/api/orders/ORDER_ID" -H "Authorization: Bearer TOKEN"
Order Status Transitions
Valid order status transitions by role:
From StatusTo StatusAllowed Roles
placedaccepted
vendor
placedcancelled
customervendoradmin
acceptedpreparing
vendor
acceptedcancelled
vendoradmin
preparingready
vendor
readypicked_up
rider
picked_updelivered
rider
Error Responses
Standard error response format:
{
  "success": false,
  "error": "Error message",
  "code": "ERROR_CODE"
}
401Unauthorized- Missing or invalid authentication
403Forbidden- Insufficient permissions for this action
404Not Found- Resource does not exist
400Bad Request- Invalid request body or parameters
409Conflict- Resource conflict (e.g., duplicate entry)
422Unprocessable- Validation error in request data
500Server Error- Internal server error
💳 Payment Integration Guide
Complete guide for implementing Paystack payments in the NearbyMe mobile app. Covers the full flow from checkout to confirmation.

Overview

The payment gateway is Paystack (Nigerian). There are no webhooks involved in the mobile flow — verification is entirely client-driven. The mobile app calls the verify endpoint after the user completes payment inside the Paystack UI.

1. Customer taps "Proceed to Checkout"
2. POST /api/orders → creates order (status: placed)
3. POST /api/payments → initializes Paystack, returns access_code + reference
4. Open Paystack UI → customer pays (card / bank transfer / USSD)
5. Paystack closes / fires success callback
6. POST /api/payments/verify → verifies with Paystack, updates payment + order
7. Show success screen → order status is now accepted
Step 1

Create the Order

Must happen before payment. Call this when the customer taps "Proceed to Checkout".

POST /api/orders
Authorization: Bearer <customer_token>

{
  "delivery_address": "12 Olu Adesanya Street, Lagos",
  "delivery_latitude": 6.5244,
  "delivery_longitude": 3.3792,
  "special_instructions": "Leave at gate"   // optional
}

// Response
{
  "success": true,
  "data": {
    "id": "order-uuid-here",       // ← SAVE THIS
    "status": "placed",
    "total_amount": 24000,         // ← SAVE THIS
    "subtotal": 22000,
    "delivery_fee": 1500,
    "service_fee": 500
  }
}
Step 2

Initialize Payment

Calls Paystack internally and returns everything the mobile app needs to open the payment UI.

payment_method accepted values:

cardPaystack card payment only
bank_transferBank transfer + bank channels
ussdUSSD only
cash_on_deliveryNo Paystack — skips verification entirely
POST /api/payments
Authorization: Bearer <customer_token>

{
  "order_id": "order-uuid-here",
  "payment_method": "card",
  "callback_url": "nearbyme://payment-callback"  // your app deep link
}

// Response
{
  "success": true,
  "data": {
    "id": "payment-uuid-here",                      // ← SAVE → payment_id for verify
    "stripe_payment_intent_id": "CHW-1716-abc123",  // ← SAVE → reference for verify
    "paystack_access_code": "0peioxfhpn",           // ← pass to Paystack SDK
    "paystack_authorization_url": "https://checkout.paystack.com/0peioxfhpn",
    "amount": 24000,
    "status": "pending"
  }
}
⚠️ Important naming note: The field stripe_payment_intent_id stores the Paystack reference string — not a Stripe ID. This is a naming artifact. Treat this value as your Paystack reference.
Step 3

Open Paystack Checkout

Open the Paystack UI using the paystack_access_code from Step 2. Use an in-app WebView or the platform SDK — do not open the device browser.

Flutter (flutter_paystack)

final charge = Charge()
  ..accessCode = payment.paystackAccessCode
  ..reference  = payment.reference;

final response = await _paystack.checkout(
  context,
  method: CheckoutMethod.selectable,
  charge: charge,
);

if (response.status == true) {
  // call verifyPayment()
}

React Native (WebView)

<WebView
  source={{ uri: authorizationUrl }}
  onNavigationStateChange={(nav) => {
    if (nav.url.startsWith(
      'nearbyme://payment-callback'
    )) {
      closeWebView()
      verifyPayment()
    }
  }}
/>
Step 4

Verify Payment

Call this immediately after the Paystack SDK fires its success callback. Do not wait for user action. Block the UI during this call.

POST /api/payments/verify
Authorization: Bearer <customer_token>

{
  "reference": "CHW-1716-abc123",      // stripe_payment_intent_id from Step 2
  "payment_id": "payment-uuid-here"    // id from Step 2
}

// Success Response
{
  "success": true,
  "data": {
    "id": "payment-uuid-here",
    "status": "completed",
    "order_id": "order-uuid-here",
    "amount": 24000
  }
}

// Failure Response (abandoned / declined)
{
  "success": false,
  "error": "Payment abandoned. Please try again."
}

What the API does internally:

1. Fetches payment record from DB

2. Fetches order separately (avoids RLS join issue)

3. Calls Paystack live API to verify the reference

4. Confirms amount paid ≥ order total (in kobo)

5. Updates payment status → completed using service-role client (bypasses RLS)

6. Advances order from placedaccepted

7. Creates a payment_success notification for the customer

Alternate Flow

Cash on Delivery

No Paystack involved. Skip Steps 3 and 4 entirely.

POST /api/payments
{
  "order_id": "order-uuid-here",
  "payment_method": "cash_on_delivery"
}

// Returns payment with status: "pending"
// Order moves forward when rider marks it delivered
// No verification step needed

State the App Must Track

{
  orderId:          string,   // from POST /api/orders
  paymentId:        string,   // from POST /api/payments → data.id
  reference:        string,   // from POST /api/payments → data.stripe_payment_intent_id
  accessCode:       string,   // pass to Paystack SDK
  status: 'idle' | 'creating_order' | 'initializing'
        | 'awaiting_paystack' | 'verifying' | 'success' | 'failed'
}

// Persist paymentId + reference to local storage immediately after Step 2
// so an interrupted payment can be resumed on app restart

Loading & Error States

StateWhat to show
POST /api/payments in progressSpinner: "Setting up payment…"
WebView / Paystack SDK openPaystack handles its own loader
POST /api/payments/verify in progressFull-screen overlay: "Confirming payment… Do not close the app"
verify → 400"Payment unsuccessful. Tap to try again." — safe to retry from Step 2
verify → 500"Something went wrong. If you were charged, contact support."
verify → 200Navigate to Payment Success screen

Edge Cases

App closed during payment

On restart, check SharedPreferences / AsyncStorage for a saved paymentId + reference. If found, prompt the user to resume and call verify. The endpoint is idempotent — safe to call again.

Verify called twice (double-tap / retry)

The verify endpoint is fully idempotent. If payment is already completed it returns the existing record and safely re-advances the order. No duplicate charges possible.

Amount mismatch

If Paystack reports a lower amount than the order total, verify returns 400 "Payment amount mismatch". Show an error and a support contact message — do not auto-retry.

Stale pending payment for same order

The initialize endpoint auto-deletes any existing pending payment for the order and creates a fresh Paystack session. Retrying Step 2 for the same order is always safe.

Network drop during verify

Retry up to 3× with exponential backoff. Idempotency makes this safe. After 3 failures show the support message.

Common Mistakes to Avoid

❌ Mistake✅ Fix
Using authorization_url as the referencereference = stripe_payment_intent_id from the init response
Opening authorization_url in the device browserUse a WebView or Paystack SDK — you must intercept the redirect
Calling verify before Paystack closesOnly call verify inside the SDK onSuccess callback or after redirect detected
Navigating away during verifyBlock the UI — partial verify causes stuck orders
Showing success based on WebView close aloneAlways confirm with verify endpoint — closed ≠ success
Not persisting paymentId + referenceSave to local storage immediately after Step 2 in case app is killed

No Webhooks in the Mobile Flow

A webhook handler type is defined in the codebase but no webhook endpoint exists yet. The entire payment flow is client-driven — the mobile app is responsible for calling verify. Do not wait for a server push.

Restaurants

Manage restaurants and their settings

GET/api/restaurants
List all restaurants
POST/api/restaurants
vendor
Create a restaurant
GET/api/restaurants/{id}
Get restaurant details
PATCH/api/restaurants/{id}
vendor
Update restaurant
PATCH/api/restaurants/{id}/status
vendor
Toggle restaurant open/closed

Cart

Shopping cart management

GET/api/cart
customer
Get current cart
POST/api/cart
customer
Add item to cart
DELETE/api/cart
customer
Clear cart
PATCH/api/cart/items/{id}
customer
Update cart item
DELETE/api/cart/items/{id}
customer
Remove cart item
POST/api/cart/promo
customer
Apply promo code
DELETE/api/cart/promo
customer
Remove promo code

Orders

Order management and tracking

GET/api/orders
customervendorrideradmin
List orders
POST/api/orders
customer
Create order from cart
GET/api/orders/{id}
customervendorrideradmin
Get order details
PATCH/api/orders/{id}/status
vendorrideradmin
Update order status
POST/api/orders/{id}/assign-rider
vendoradmin
Assign rider to order

Riders

Rider management and tracking

GET/api/riders
vendoradmin
List available riders
POST/api/riders
rider
Create rider profile
GET/api/riders/me
rider
Get my rider profile
PATCH/api/riders/me
rider
Update my rider profile
PATCH/api/riders/me/status
rider
Update availability status
POST/api/riders/me/location
rider
Update current location
GET/api/riders/me/earnings
rider
Get my earnings

Payments

Payment processing and earnings

POST/api/payments
customer
Create payment
POST/api/payments/{id}/confirm
customeradmin
Confirm payment
GET/api/vendor/earnings
vendor
Get vendor earnings

Customer Features

Endpoints for the customer mobile app — profile, addresses, search, favorites, notifications, reviews, and order utilities.

GET/api/users/me
customer
Get my profile
PATCH/api/users/me
customer
Update my profile
GET/api/users/me/notification-preferences
customer
Get notification preferences
PATCH/api/users/me/notification-preferences
customer
Update notification preferences
GET/api/addresses
customer
List saved addresses
POST/api/addresses
customer
Add delivery address
DELETE/api/addresses/{id}
customer
Delete address
GET/api/categories
List global categories
GET/api/search
Unified search
GET/api/search/suggestions
Search autocomplete
GET/api/search/history
customer
Get search history
DELETE/api/search/history
customer
Clear search history
GET/api/favorites
customer
Get favorites
POST/api/favorites
customer
Add to favorites
DELETE/api/favorites/{id}
customer
Remove from favorites
GET/api/notifications
customer
Get notifications
PATCH/api/notifications/{id}/read
customer
Mark notification as read
GET/api/restaurants/{id}/reviews
Get vendor reviews
POST/api/restaurants/{id}/reviews
customer
Rate a vendor
POST/api/riders/{id}/reviews
customer
Rate a rider
GET/api/orders/{id}/track
customer
Track order (live)
POST/api/orders/{id}/confirm
customer
Confirm delivery with code
POST/api/orders/{id}/reorder
customer
Reorder
GET/api/orders/{id}/receipt
customer
Get order receipt
GET/api/beneficiaries
customer
List saved gift recipients
POST/api/beneficiaries
customer
Save a gift recipient
PATCH/api/beneficiaries/{id}
customer
Update saved recipient
DELETE/api/beneficiaries/{id}
customer
Remove saved recipient
GET/api/restaurants/{id}/banners
Get vendor promo banners
GET/api/banners
Home screen promotional banners
GET/api/featured-dishes
Featured dishes
GET/api/explore
Restaurants to explore
GET/api/recommendations
Personalised recommendations
GET/api/home-sections
Custom home screen sections

Admin

Admin-only endpoints. All require an admin JWT. Login: admin@nearbyme.io / Admin@NearbyMe2024!

GET/api/admin/dashboard
admin
Dashboard stats
GET/api/admin/analytics
admin
Analytics by period
GET/api/admin/users
admin
List all users
GET/api/admin/users/{id}
admin
Get user details
PATCH/api/admin/users/{id}
admin
Update user
DELETE/api/admin/users/{id}
admin
Delete user
GET/api/admin/vendors
admin
List all vendors/stores
GET/api/admin/vendors/{id}
admin
Get vendor details
PATCH/api/admin/vendors/{id}
admin
Update or suspend vendor
GET/api/admin/riders
admin
List all riders
PATCH/api/admin/riders/{id}
admin
Update or suspend rider
GET/api/admin/orders
admin
List all orders
GET/api/admin/orders/{id}
admin
Get order details
PATCH/api/admin/orders/{id}
admin
Update order
GET/api/admin/earnings
admin
List earnings
GET/api/admin/payouts
admin
List payouts
POST/api/admin/payouts
admin
Create payout
PATCH/api/admin/payouts/{id}
admin
Update payout status
GET/api/admin/disputes
admin
List disputes
PATCH/api/admin/disputes/{id}
admin
Resolve dispute
GET/api/admin/promo-codes
admin
List promo codes
POST/api/admin/promo-codes
admin
Create promo code
PATCH/api/admin/promo-codes/{id}
admin
Update promo code
DELETE/api/admin/promo-codes/{id}
admin
Delete promo code
GET/api/admin/audit-logs
admin
View audit logs
GET/api/admin/banners
List promotional banners
POST/api/admin/banners
Create promotional banner
GET/api/admin/banners/{id}
Get banner by ID
PATCH/api/admin/banners/{id}
Update promotional banner
DELETE/api/admin/banners/{id}
Delete promotional banner
GET/api/admin/home-sections
List custom home sections
POST/api/admin/home-sections
Create custom home section
PATCH/api/admin/home-sections/{id}
Update / reorder home section
DELETE/api/admin/home-sections/{id}
Delete home section
POST/api/admin/home-sections/{id}/items
Add item to home section
PATCH/api/admin/home-sections/{id}/items/{itemId}
Reorder item in section
DELETE/api/admin/home-sections/{id}/items/{itemId}
Remove item from section