🌲 BackWood API Documentation

Welcome to the BackWood API documentation. This guide will help you integrate with our backend services.

⚠️ Error Handling

All API endpoints follow a consistent error handling pattern:

Error Response Format

{
  "status": "error" | "fail",
  "message": "Human readable error message",
  "errors": [
    // Optional array of specific error details
  ],
  "stack": "Stack trace (development only)"
}
Common Error Status Codes
  • 400 - Bad Request (validation errors)
  • 401 - Unauthorized (authentication required)
  • 403 - Forbidden (insufficient permissions)
  • 404 - Not Found
  • 409 - Conflict (e.g., duplicate entry)
  • 422 - Unprocessable Entity
  • 500 - Internal Server Error

Error Handling in React Native

// API error handling utility
const handleApiError = (error) => {
  if (error.response) {
    // Server responded with error
    const { status, data } = error.response;
    
    switch (status) {
      case 401:
        // Handle authentication error
        navigation.navigate('Login');
        break;
      case 403:
        Alert.alert('Access Denied', data.message);
        break;
      case 422:
        // Handle validation errors
        const errors = data.errors?.join('\n');
        Alert.alert('Validation Error', errors || data.message);
        break;
      default:
        Alert.alert('Error', data.message || 'Something went wrong');
    }
  } else if (error.request) {
    // Network error
    Alert.alert('Network Error', 'Please check your internet connection');
  } else {
    // Other errors
    Alert.alert('Error', error.message || 'Something went wrong');
  }
};

🔐 Authentication

All authenticated endpoints require a Bearer token in the Authorization header.

POST /api/auth/register

Register a new user account and sends welcome email with verification code.

Request:
{
  "email": "user@example.com",
  "password": "StrongPass1!",
  "firstName": "John",
  "lastName": "Doe",
  "phone": "+1234567890"
}
Response:
{
  "message": "User created successfully. Please verify your email.",
  "token": "jwt_token",
  "user": {
    "id": "user_id",
    "email": "user@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "phone": "+1234567890",
    "status": "PENDING",
    "emailVerified": false,
    "phoneVerified": false,
    "kycVerified": false
  }
}
Note: A welcome email with verification code will be sent automatically.

POST /api/auth/login

Login to get authentication token.

{
  "email": "user@example.com",
  "password": "StrongPass1!"
}

POST /api/auth/logout

Invalidate current token.

// React Native Example
const logout = async () => {
  try {
    await axios.post('/api/auth/logout', {}, {
      headers: { Authorization: `Bearer ${token}` }
    });
    // Clear local storage
    await AsyncStorage.removeItem('token');
    navigation.replace('Login');
  } catch (error) {
    handleApiError(error);
  }
};

✉️ Email & Verification

Email verification system with templated emails.

POST /api/verification/send

Send a verification code to user's email.

{
  "userId": "user_id",
  "type": "EMAIL"
}
Response:
{
  "message": "Verification code sent to your email"
}

POST /api/verification/verify

Verify email using received code.

{
  "userId": "user_id",
  "type": "EMAIL",
  "code": "123456"
}
Response:
{
  "message": "Email verified successfully"
}

Email Templates

Available email templates:

  • Welcome Email
    • Sent upon registration
    • Includes verification code
    • Personalized with user's name
  • Verification Email
    • 6-digit verification code
    • 15-minute expiration
    • Personalized greeting
Template Variables:
{
  "firstName": "User's first name",
  "verificationCode": "6-digit code",
  "appName": "Application name from env",
  "year": "Current year"
}

💬 Messages

POST /api/messages/send

Send a message to another user, optionally about a specific listing.

{
  "receiverId": "user_id",
  "content": "Hello, is this item still available?",
  "conversationId": "optional_conversation_id",
  "listingId": "optional_listing_id"
}
Response:
{
  "status": "success",
  "message": {
    "id": "message_id",
    "content": "Hello, is this item still available?",
    "sender": {
      "id": "sender_id",
      "firstName": "John",
      "lastName": "Doe",
      "profileImage": "url"
    },
    "listing": {
      "id": "listing_id",
      "title": "Item Title",
      "price": 100
    },
    "createdAt": "2024-01-01T00:00:00Z",
    "read": false
  }
}

GET /api/messages/conversation/{otherId}

Get conversation history with another user.

// React Native Example
const fetchConversation = async (userId) => {
  try {
    const response = await axios.get(`/api/messages/conversation/${userId}`, {
      headers: { Authorization: `Bearer ${token}` },
      params: {
        page: currentPage,
        limit: 20
      }
    });
    
    setMessages(response.data.messages);
    setPagination(response.data.pagination);
    
  } catch (error) {
    handleApiError(error);
  }
};

GET /api/messages/unread/count

Get number of unread messages.

// React Native Example with WebSocket
useEffect(() => {
  // Initial fetch
  fetchUnreadCount();
  
  // Listen for new messages
  socket.on('newMessage', () => {
    fetchUnreadCount();
  });
  
  return () => socket.off('newMessage');
}, []);

GET /api/messages/conversations

Get all conversations for the authenticated user

Response:
{
  "status": "success",
  "conversations": [
    {
      "id": "conversation_id",
      "otherUser": {
        "id": "user_id",
        "firstName": "John",
        "lastName": "Doe",
        "profileImage": "url"
      },
      "lastMessage": {
        "content": "Hello there!",
        "createdAt": "2024-01-01T00:00:00Z",
        "read": false
      },
      "lastMessageTime": "2024-01-01T00:00:00Z"
    }
  ],
  "pagination": {
    "total": 10,
    "pages": 1,
    "currentPage": 1,
    "limit": 20
  }
}

Sending Messages with Conversation ID

When sending messages, you can include an existing conversationId:

{
  "receiverId": "user_id",
  "content": "Hello there!",
  "conversationId": "existing_conversation_id"
}
WebSocket Implementation
// Initialize socket with conversation
socket.on('connect', () => {
  // Join conversation room
  socket.emit('joinConversation', conversationId);
});

// Send message with conversation
socket.emit('message', {
  receiverId: otherUserId,
  content: messageText,
  conversationId: currentConversationId
});

// Listen for messages in conversation
socket.on('message:conversation', (message) => {
  if (message.conversationId === currentConversationId) {
    // Update conversation messages
    setMessages(prev => [...prev, message]);
  }
});
Note: If no conversationId is provided, the API will:
  • Check for existing conversation between users
  • Create new conversation if none exists
  • Return the conversation ID in the response

👑 Admin

POST /api/admin/login

Admin login with enhanced security.

{
  "email": "admin@example.com",
  "password": "AdminPass1!",
  "totpCode": "123456"  // If 2FA enabled
}

GET /api/admin/users

Get all users with filtering and pagination.

// React Native Admin Panel Example
const fetchUsers = async () => {
  try {
    const response = await axios.get('/api/admin/users', {
      headers: { Authorization: `Bearer ${adminToken}` },
      params: {
        status: filterStatus,
        role: filterRole,
        search: searchQuery,
        page: currentPage,
        limit: 20
      }
    });
    
    setUsers(response.data.users);
    setPagination(response.data.pagination);
    
  } catch (error) {
    handleApiError(error);
  }
};

PUT /api/admin/users/{userId}

Update user status or role.

{
  "status": "ACTIVE" | "SUSPENDED" | "BANNED",
  "role": "USER" | "MODERATOR" | "ADMIN"
}

🔌 WebSocket Integration

Real-time Messaging Example

// React Native WebSocket Setup
import { io } from 'socket.io-client';

const setupWebSocket = (token) => {
  const socket = io('YOUR_WEBSOCKET_URL', {
    auth: { token }
  });

  socket.on('connect', () => {
    console.log('Connected to WebSocket');
  });

  socket.on('newMessage', (message) => {
    // Update messages in real-time
    setMessages(prev => [...prev, message]);
    // Show notification
    showNotification(message);
  });

  socket.on('messageRead', ({ messageId }) => {
    // Update message read status
    setMessages(prev => prev.map(msg => 
      msg.id === messageId ? {...msg, read: true} : msg
    ));
  });

  return socket;
};
WebSocket Events
  • newMessage - Received when a new message arrives
  • messageRead - When messages are marked as read
  • userOnline - When a user comes online
  • userOffline - When a user goes offline
  • typing - When someone is typing

📱 React Native Integration

API Client Setup

// api/client.js
import axios from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage';

const api = axios.create({
  baseURL: 'YOUR_API_URL',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
});

// Add token to requests
api.interceptors.request.use(async (config) => {
  const token = await AsyncStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Handle token expiration
api.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response?.status === 401) {
      await AsyncStorage.removeItem('token');
      // Navigate to login
      navigation.replace('Login');
    }
    return Promise.reject(error);
  }
);

Custom Hooks Example

// hooks/useMessages.js
export const useMessages = (conversationId) => {
  const [messages, setMessages] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchMessages = async () => {
      try {
        setLoading(true);
        const response = await api.get(
          `/messages/conversation/${conversationId}`
        );
        setMessages(response.data.messages);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchMessages();
  }, [conversationId]);

  const sendMessage = async (content) => {
    try {
      const response = await api.post('/messages/send', {
        receiverId: conversationId,
        content
      });
      setMessages(prev => [...prev, response.data.message]);
      return response.data.message;
    } catch (err) {
      setError(err);
      throw err;
    }
  };

  return { messages, loading, error, sendMessage };
};

👤 Profile

Manage user profiles and settings.

GET /api/profile

Get current user's profile

Response:
{
  "user": {
    "id": "user_id",
    "email": "user@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "phone": "+1234567890",
    "avatar": "url_to_avatar",
    "status": "ACTIVE",
    "emailVerified": true,
    "phoneVerified": true,
    "kycVerified": false,
    "createdAt": "2024-01-01T00:00:00Z",
    "lastLogin": "2024-01-01T00:00:00Z"
  }
}

PUT /api/profile

Update user profile

Request:
{
  "firstName": "John",
  "lastName": "Doe",
  "phone": "+1234567890",
  "avatar": "base64_image_data"
}

📦 Listings

Manage product listings and searches.

POST /api/listings

Create a new listing

Request:
{
  "title": "Product Title",
  "description": "Detailed description",
  "price": 99.99,
  "category": "ELECTRONICS",
  "condition": "NEW",
  "images": ["base64_image_1", "base64_image_2"],
  "location": {
    "latitude": 123.456,
    "longitude": 789.012
  },
  "tags": ["tag1", "tag2"]
}

GET /api/listings

Search listings with filters

Query Parameters:
  • search - Search term
  • category - Filter by category
  • minPrice - Minimum price
  • maxPrice - Maximum price
  • condition - Item condition
  • radius - Search radius in km
  • lat - Latitude for location-based search
  • lng - Longitude for location-based search
  • page - Page number (default: 1)
  • limit - Items per page (default: 20)

💬 Messages

Real-time messaging system using WebSocket.

WebSocket Events

// Connect to WebSocket
const socket = io(WEBSOCKET_URL, {
  auth: { token: userToken }
});

// Listen for new messages
socket.on('newMessage', (message) => {
  console.log('New message:', message);
});

// Send a message
socket.emit('sendMessage', {
  recipientId: 'user_id',
  content: 'Message content',
  listingId: 'listing_id' // Optional, for listing-related messages
});

💰 Offers

Manage buying and selling offers for listings.

POST /api/offers

Make an offer on a listing

Request:
{
  "listingId": "listing_id",
  "amount": 95.50,
  "message": "Is this still available? I'm interested.",
  "type": "BUY"
}
Response:
{
  "id": "offer_id",
  "status": "PENDING",
  "createdAt": "2024-01-01T00:00:00Z",
  "amount": 95.50,
  "message": "Is this still available? I'm interested."
}

PUT /api/offers/:offerId

Update offer status (accept/reject)

Request:
{
  "status": "ACCEPTED" | "REJECTED",
  "message": "Optional response message"
}

⭐ Reviews

User review and rating system.

POST /api/reviews

Create a review for a user after successful transaction

Request:
{
  "userId": "user_id",
  "rating": 5,
  "comment": "Great buyer! Quick payment and good communication.",
  "transactionId": "transaction_id"
}

GET /api/reviews/user/:userId

Get reviews for a specific user

Query Parameters:
  • page - Page number (default: 1)
  • limit - Reviews per page (default: 10)
  • sort - Sort order (recent/rating)

🪪 KYC

Know Your Customer verification system.

POST /api/kyc/submit

Submit KYC verification documents

Request:
{
  "documentType": "ID_CARD" | "PASSPORT" | "DRIVERS_LICENSE",
  "idImageUrl": "url_to_id_image",
  "selfieImageUrl": "url_to_selfie_image"
}
Note: Images must be uploaded separately using the image upload endpoint before submitting KYC.

GET /api/kyc/status

Check KYC verification status

Response:
{
  "status": "PENDING" | "VERIFIED" | "REJECTED",
  "message": "Optional status message",
  "submittedAt": "2024-01-01T00:00:00Z",
  "verifiedAt": "2024-01-02T00:00:00Z"
}

🖼️ Image Guidelines

Guidelines and endpoints for image handling.

POST /api/images/upload

Upload images for listings or KYC

Request:
{
  "image": "base64_encoded_image",
  "type": "LISTING" | "KYC_ID" | "KYC_SELFIE",
  "metadata": {
    "width": 1024,
    "height": 768,
    "size": 1048576
  }
}
Image Requirements
  • Maximum file size: 5MB
  • Supported formats: JPG, PNG
  • Minimum dimensions: 640x480 pixels
  • Maximum dimensions: 4096x4096 pixels
  • Aspect ratio: Between 4:3 and 16:9

Image Processing

  • Images are automatically compressed and optimized
  • EXIF data is stripped for privacy
  • Multiple sizes are generated for responsive display
  • Watermarking is applied to listing images

📑 Categories

Browse and manage listing categories.

GET /api/categories

Get all available categories

Response:
{
  "status": "success",
  "categories": [
    {
      "id": "category_id",
      "name": "Real Estate",
      "slug": "real-estate",
      "description": "Properties for rent and sale"
    },
    {
      "name": "Vehicles",
      "slug": "vehicles",
      "description": "Cars, motorcycles, and other vehicles"
    }
  ]
}

GET /api/categories/:slug/listings

Get listings by category

Query Parameters:
  • page - Page number (default: 1)
  • limit - Listings per page (default: 20)
  • sort - Sort order (recent/price)