A production-ready Flask backend for Homester's AI-powered real estate platform. This API provides complete authentication, property search, and AI chat functionality for frontend applications.
This backend provides three authentication methods and AI-powered real estate features. All endpoints return JSON and use standard HTTP status codes.
http://localhost:5000https://your-production-domain.com/docs/ - Interactive API testing/test-google-token - Get real tokens for OAuth testingStep 1: Send OTP
javascript
const response = await fetch('/api/firebase-auth/otp/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: '[email protected]' })
});
// Returns: {"message": "OTP sent successfully"}
Step 2: Verify OTP ```javascript const response = await fetch('/api/firebase-auth/otp/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier: '[email protected]', otp: '123456' // User enters this code }) });
const { access_token, refresh_token, user } = await response.json(); // Store tokens for authenticated requests localStorage.setItem('access_token', access_token); localStorage.setItem('refresh_token', refresh_token); ```
Step 1: Get Firebase Config
javascript
const config = await fetch('/api/firebase-auth/config').then(r => r.json());
Step 2: Initialize Firebase & Sign In ```javascript import { initializeApp } from 'firebase/app'; import { getAuth, signInWithPopup, GoogleAuthProvider } from 'firebase/auth';
const app = initializeApp(config); const auth = getAuth(app); const provider = new GoogleAuthProvider();
// Google sign-in const result = await signInWithPopup(auth, provider); const idToken = await result.user.getIdToken(); ```
Step 3: Convert to Backend JWT ```javascript const response = await fetch('/api/firebase-auth/oauth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ idToken }) });
const { access_token, refresh_token, user } = await response.json(); ```
Step 1: Initialize Firebase Phone Auth ```javascript import { RecaptchaVerifier, signInWithPhoneNumber } from 'firebase/auth';
// Setup reCAPTCHA window.recaptchaVerifier = new RecaptchaVerifier(auth, 'recaptcha-container', { size: 'invisible' });
// Send SMS const confirmationResult = await signInWithPhoneNumber( auth, '+1234567890', window.recaptchaVerifier ); ```
Step 2: Verify SMS Code ```javascript const result = await confirmationResult.confirm('123456'); // SMS code const idToken = await result.user.getIdToken();
// Convert to backend JWT (same as OAuth step 3) const response = await fetch('/api/firebase-auth/oauth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ idToken }) }); ```
After authentication, include the access token in all API requests:
javascript
const response = await fetch('/api/properties/search?city=San Francisco', {
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
}
});
Token Refresh (when access token expires): ```javascript const response = await fetch('/api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refresh_token }) });
const { access_token: newToken } = await response.json(); ```
``javascript
const properties = await fetch('/api/properties/search?' + new URLSearchParams({
city: 'San Francisco',
max_price: '1000000',
bedrooms: '2',
bathrooms: '2',
property_type: 'house'
}), {
headers: { 'Authorization':Bearer ${access_token}` }
}).then(r => r.json());
// Returns: { properties: [...], pagination: {...}, total: 150 } ```
javascript
const properties = await fetch('/api/properties/search', {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: "modern 3-bedroom house near good schools",
city: "Austin",
max_price: 800000
})
}).then(r => r.json());
javascript
const property = await fetch('/api/properties/123', {
headers: { 'Authorization': `Bearer ${access_token}` }
}).then(r => r.json());
``javascript
// Add to favorites
await fetch('/api/properties/123/favorite', {
method: 'POST',
headers: { 'Authorization':Bearer ${access_token}` }
});
// Remove from favorites
await fetch('/api/properties/123/favorite', {
method: 'DELETE',
headers: { 'Authorization': Bearer ${access_token} }
});
// Get user's favorites
const favorites = await fetch('/api/properties/favorites', {
headers: { 'Authorization': Bearer ${access_token} }
}).then(r => r.json());
```
``javascript
const response = await fetch('/api/chat/message', {
method: 'POST',
headers: {
'Authorization':Bearer ${access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: "I'm looking for a family home in Austin under $500k",
session_id: null // Creates new session
})
}).then(r => r.json());
// Returns: { response: "AI response", properties: [...], session_id: "uuid" } ```
javascript
const response = await fetch('/api/chat/message', {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: "Show me houses with pools",
session_id: "existing-session-id"
})
}).then(r => r.json());
``javascript
const sessions = await fetch('/api/chat/sessions', {
headers: { 'Authorization':Bearer ${access_token}` }
}).then(r => r.json());
// Get specific session with messages
const session = await fetch('/api/chat/sessions/session-id', {
headers: { 'Authorization': Bearer ${access_token} }
}).then(r => r.json());
```
```javascript import { useState, useEffect } from 'react';
export function useAuth() { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true);
const login = async (email, otp) => { const response = await fetch('/api/firebase-auth/otp/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier: email, otp }) });
if (response.ok) {
const { access_token, user } = await response.json();
localStorage.setItem('access_token', access_token);
setUser(user);
return true;
}
return false;
};
const logout = () => { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); setUser(null); };
return { user, login, logout, loading }; } ```
```javascript export default { data() { return { properties: [], loading: false, filters: { city: '', max_price: '', bedrooms: '' } } }, methods: { async searchProperties() { this.loading = true; const token = localStorage.getItem('access_token');
const response = await fetch('/api/properties/search?' +
new URLSearchParams(this.filters), {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
this.properties = data.properties;
this.loading = false;
}
} } ```
All API responses follow consistent patterns:
Success Response:
json
{
"data": {...},
"message": "Success"
}
Error Response:
json
{
"message": "Error description",
"error": "ERROR_CODE",
"status_code": 400
}
Common Error Codes:
- 401 - Invalid or expired token
- 400 - Invalid request data
- 429 - Rate limit exceeded
- 500 - Server error
```javascript // 1. Send OTP await fetch('/api/firebase-auth/otp/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: userEmail }) });
// 2. User enters OTP, verify it const auth = await fetch('/api/firebase-auth/otp/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier: userEmail, otp: userOtp }) }).then(r => r.json());
// 3. Store token and start using API localStorage.setItem('access_token', auth.access_token); ```
GET /api/firebase-auth/config - Get Firebase configurationPOST /api/firebase-auth/otp/send - Send OTP via emailPOST /api/firebase-auth/otp/verify - Verify OTP codePOST /api/firebase-auth/oauth/login - OAuth login with FirebasePOST /api/firebase-phone/send-verification - Initiate phone verificationPOST /api/auth/refresh - Refresh access tokenGET /api/properties/search - Search properties with filtersGET /api/properties/{id} - Get property by IDGET /api/properties/types - Get available property typesGET /api/properties/favorites - Get user's favorite propertiesPOST /api/properties/{id}/favorite - Add to favoritesDELETE /api/properties/{id}/favorite - Remove from favoritesGET /api/chat/sessions - Get user's chat sessionsGET /api/chat/sessions/{id} - Get session with messagesPOST /api/chat/message - Send message and get AI responseDELETE /api/chat/sessions/{id} - Delete chat sessionGET /api/auth/profile - Get current user profilePUT /api/auth/profile - Update user profilePOST /api/auth/register - Traditional registration (optional)POST /api/auth/login - Traditional login (optional)FIREBASE_SERVICE_ACCOUNT_KEY environment variableDATABASE_URL for PostgreSQLJWT_SECRET_KEYREDIS_URL for improved rate limitingOPENAI_API_KEY for AI chat featuresThe system now supports professional email delivery for OTP codes:
bash
export SENDGRID_API_KEY="your-sendgrid-api-key"
export FROM_EMAIL="[email protected]"
export FROM_NAME="Homester"
bash
export SMTP_SERVER="smtp.gmail.com"
export SMTP_PORT="587"
export SMTP_USERNAME="[email protected]"
export SMTP_PASSWORD="your-app-password"
[email protected]/docs/ for interactive testing/test-google-token for OAuth developmentThat's it! Your frontend now has access to AI-powered real estate search, user authentication, and conversational property assistance. Start with the Email OTP method for immediate testing, then add Google OAuth and Phone Auth as needed.
docker compose up -d --build api docker compose logs -f api
docker compose down -v
docker compose pull
docker compose build --no-cache
docker compose up -d --force-recreate
docker compose logs