Integration Guide
Complete integration guide for the Esto platform
Integration Guide
This guide provides comprehensive instructions for integrating the Esto platform into your real estate applications. We'll cover everything from basic setup to advanced features.
🚀 Quick Start
1. Prerequisites
Before integrating, ensure you have:
- Node.js 18+ or Python 3.8+
- Docker and Docker Compose
- API Key from Esto platform
- PostgreSQL (optional, for local development)
2. Environment Setup
Create a .env file in your project root:
# Esto API Configuration
ESTO_API_BASE_URL=http://localhost:8000
ESTO_API_KEY=your_api_key_here
# Database Configuration (if using local)
DATABASE_URL=postgresql://user:password@localhost:5432/esto
# Optional: Redis for caching
REDIS_URL=redis://localhost:63793. Install SDK
JavaScript/TypeScript
npm install @esto/sdk
# or
yarn add @esto/sdkPython
pip install esto-sdk📞 Basic Integration
Contact Management
Creating a Contact
import { EstoClient } from '@esto/sdk';
const client = new EstoClient({
apiKey: process.env.ESTO_API_KEY,
baseUrl: process.env.ESTO_API_BASE_URL,
});
// Create a new contact
const contact = await client.contacts.create({
email: 'ahmed.benali@email.com',
fullName: 'Ahmed Benali',
phone: '+213 123 456 789',
age: 35,
familySituation: 'married',
children: 2,
wilaya: 'Alger',
baladiya: 'Hydra',
minBudgets: [50000000, 80000000],
maxBudgets: [70000000, 100000000],
minSurface: [80, 120],
maxSurface: [100, 150],
preferedPropertyType: 'apartment',
bedrooms: 3,
options: ['parking', 'elevator', 'balcony'],
});
console.log('Contact created:', contact.id);Python Example
from esto_sdk import EstoClient
client = EstoClient(
api_key=os.getenv('ESTO_API_KEY'),
base_url=os.getenv('ESTO_API_BASE_URL')
)
# Create a new contact
contact = client.contacts.create({
'email': 'ahmed.benali@email.com',
'fullName': 'Ahmed Benali',
'phone': '+213 123 456 789',
'age': 35,
'familySituation': 'married',
'children': 2,
'wilaya': 'Alger',
'baladiya': 'Hydra',
'minBudgets': [50000000, 80000000],
'maxBudgets': [70000000, 100000000],
'minSurface': [80, 120],
'maxSurface': [100, 150],
'preferedPropertyType': 'apartment',
'bedrooms': 3,
'options': ['parking', 'elevator', 'balcony']
})
print(f'Contact created: {contact["id"]}')Property Management
Creating a Property
// Create a new property
const property = await client.estates.create({
title: 'Modern Apartment in Hydra',
description: 'Beautiful 3-bedroom apartment with sea view',
price: 75000000,
surface: 120,
bedrooms: 3,
bathrooms: 2,
propertyType: 'apartment',
wilaya: 'Alger',
baladiya: 'Hydra',
images: ['https://example.com/image1.jpg', 'https://example.com/image2.jpg'],
});
console.log('Property created:', property.id);Listing Properties
// Get paginated list of properties
const properties = await client.estates.list({
page: 1,
limit: 20,
search: 'Hydra',
wilaya: 'Alger',
});
console.log(`Found ${properties.total} properties`);🤖 AI Matching Integration
Getting Recommendations
// Get AI-powered recommendations for a property
const recommendations = await client.matcher.match({
propertyId: property.id,
embedding: generatePropertyEmbedding(property), // Your embedding logic
});
console.log(`Found ${recommendations.matches_found} matches`);
// Process recommendations
recommendations.recommendations.forEach((match) => {
console.log(`Contact ${match.contactId}: ${match.score} - ${match.explanation}`);
});Batch Processing
// Process multiple properties at once
const propertyIds = ['e_123456', 'e_123457', 'e_123458'];
const batchResults = await Promise.all(
propertyIds.map(async (propertyId) => {
const recommendations = await client.matcher.match({
propertyId,
embedding: await getPropertyEmbedding(propertyId),
});
return {
propertyId,
matchesFound: recommendations.matches_found,
topMatches: recommendations.recommendations.slice(0, 5),
};
}),
);
console.log('Batch processing completed:', batchResults);🎯 Marketing Integration
Facebook Marketing Strategy
// Generate AI-powered marketing strategy
const strategy = await client.agents.facebookStrategy({
propertyId: property.id,
});
console.log('Target Audience:', strategy.targetAudience);
console.log('Ad Format:', strategy.adFormat);
console.log('Budget Recommendation:', strategy.budgetRecommendation);
// Use creative recommendations
strategy.creativeRecommendations.forEach((recommendation) => {
console.log('Creative:', recommendation);
});
// Use copy suggestions
strategy.copySuggestions.forEach((copy) => {
console.log('Copy:', copy);
});Property Comparison
// Compare two properties
const comparison = await client.agents.compareProperties({
property1Id: 'e_123456',
property2Id: 'e_123457',
});
console.log('Summary:', comparison.summary);
console.log('Price Analysis:', comparison.priceAnalysis);
console.log('Recommendation:', comparison.recommendation);📄 Document Generation
Generating PDF Quotes
// Generate a professional PDF quote
const quote = await client.documents.generateQuote({
propertyId: property.id,
contactId: contact.id,
quoteDetails: {
validUntil: '2024-02-15',
specialConditions: 'Subject to financing approval',
},
});
console.log('PDF Quote URL:', quote.pdfUrl);🔧 Advanced Integration
Error Handling
try {
const contact = await client.contacts.create(contactData);
console.log('Contact created successfully');
} catch (error) {
if (error.code === 'VALIDATION_ERROR') {
console.error('Validation error:', error.details);
} else if (error.code === 'CONFLICT') {
console.error('Contact already exists');
} else {
console.error('Unexpected error:', error.message);
}
}Rate Limiting
// Handle rate limiting
client.on('rateLimit', (info) => {
console.log(`Rate limited. Reset at: ${new Date(info.reset * 1000)}`);
// Implement retry logic with exponential backoff
});
// Implement retry logic
const retryWithBackoff = async (fn, maxRetries = 3) => {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.code === 'RATE_LIMIT' && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // Exponential backoff
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
};Caching
import NodeCache from 'node-cache';
const cache = new NodeCache({ stdTTL: 3600 }); // 1 hour cache
const getCachedRecommendations = async (propertyId) => {
const cacheKey = `recommendations:${propertyId}`;
// Check cache first
let recommendations = cache.get(cacheKey);
if (!recommendations) {
// Fetch from API
recommendations = await client.matcher.match({
propertyId,
embedding: await getPropertyEmbedding(propertyId),
});
// Cache the result
cache.set(cacheKey, recommendations);
}
return recommendations;
};🏗️ Webhook Integration
Setting Up Webhooks
// Configure webhooks for real-time updates
const webhookConfig = await client.webhooks.create({
url: 'https://your-app.com/webhooks/esto',
events: ['contact.created', 'property.matched', 'recommendation.generated'],
secret: 'your_webhook_secret',
});
console.log('Webhook configured:', webhookConfig.id);Webhook Handler
// Express.js webhook handler
app.post('/webhooks/esto', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['esto-signature'];
const payload = req.body;
// Verify webhook signature
if (!verifyWebhookSignature(signature, payload, process.env.ESTO_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
switch (event.type) {
case 'contact.created':
handleContactCreated(event.data);
break;
case 'property.matched':
handlePropertyMatched(event.data);
break;
case 'recommendation.generated':
handleRecommendationGenerated(event.data);
break;
}
res.status(200).send('OK');
});
function handleContactCreated(data) {
console.log('New contact created:', data.contact.id);
// Send welcome email, update CRM, etc.
}
function handlePropertyMatched(data) {
console.log('Property matched:', data.propertyId);
console.log('Matches found:', data.matchesFound);
// Send notifications, update dashboard, etc.
}📊 Analytics Integration
Tracking User Interactions
// Track user interactions for analytics
const analytics = {
trackRecommendationView: async (propertyId, contactId) => {
await client.analytics.track({
event: 'recommendation_viewed',
properties: {
propertyId,
contactId,
timestamp: new Date().toISOString(),
},
});
},
trackRecommendationClick: async (propertyId, contactId) => {
await client.analytics.track({
event: 'recommendation_clicked',
properties: {
propertyId,
contactId,
timestamp: new Date().toISOString(),
},
});
},
trackQuoteGenerated: async (propertyId, contactId) => {
await client.analytics.track({
event: 'quote_generated',
properties: {
propertyId,
contactId,
timestamp: new Date().toISOString(),
},
});
},
};🔒 Security Best Practices
API Key Management
// Use environment variables for API keys
const client = new EstoClient({
apiKey: process.env.ESTO_API_KEY,
baseUrl: process.env.ESTO_API_BASE_URL,
});
// Rotate API keys regularly
const rotateApiKey = async () => {
const newKey = await client.auth.rotateKey();
process.env.ESTO_API_KEY = newKey;
console.log('API key rotated successfully');
};Input Validation
import Joi from 'joi';
const contactSchema = Joi.object({
email: Joi.string().email().required(),
fullName: Joi.string().min(2).max(100).required(),
phone: Joi.string()
.pattern(/^\+213 \d{3} \d{3} \d{3}$/)
.required(),
age: Joi.number().integer().min(18).max(100).required(),
// ... other fields
});
const validateContact = (data) => {
const { error, value } = contactSchema.validate(data);
if (error) {
throw new Error(`Validation error: ${error.details[0].message}`);
}
return value;
};🧪 Testing Integration
Unit Tests
import { EstoClient } from '@esto/sdk';
describe('Esto Integration', () => {
let client;
beforeEach(() => {
client = new EstoClient({
apiKey: 'test_api_key',
baseUrl: 'http://localhost:8000',
});
});
test('should create a contact', async () => {
const contactData = {
email: 'test@example.com',
fullName: 'Test User',
phone: '+213 123 456 789',
age: 30,
familySituation: 'single',
wilaya: 'Alger',
minBudgets: [50000000],
maxBudgets: [70000000],
minSurface: [80],
maxSurface: [100],
preferedPropertyType: 'apartment',
bedrooms: 2,
options: ['parking'],
};
const contact = await client.contacts.create(contactData);
expect(contact.id).toBeDefined();
expect(contact.email).toBe(contactData.email);
});
test('should get recommendations', async () => {
const recommendations = await client.matcher.match({
propertyId: 'test_property_id',
embedding: [0.1, 0.2, 0.3],
});
expect(recommendations.matches_found).toBeGreaterThan(0);
expect(recommendations.recommendations).toBeDefined();
});
});Integration Tests
describe('Esto Integration Tests', () => {
test('full workflow', async () => {
// 1. Create contact
const contact = await client.contacts.create(contactData);
// 2. Create property
const property = await client.estates.create(propertyData);
// 3. Get recommendations
const recommendations = await client.matcher.match({
propertyId: property.id,
embedding: generateEmbedding(property),
});
// 4. Generate quote
const quote = await client.documents.generateQuote({
propertyId: property.id,
contactId: contact.id,
});
// 5. Verify results
expect(recommendations.matches_found).toBeGreaterThan(0);
expect(quote.pdfUrl).toBeDefined();
});
});📚 SDK Reference
JavaScript/TypeScript SDK
interface EstoClientConfig {
apiKey: string;
baseUrl: string;
timeout?: number;
retries?: number;
}
interface Contact {
id: string;
email: string;
fullName: string;
// ... other fields
}
interface Property {
id: string;
title: string;
price: number;
// ... other fields
}
interface Recommendation {
contactId: string;
score: number;
explanation: string;
}
class EstoClient {
constructor(config: EstoClientConfig);
contacts: {
create(data: CreateContactRequest): Promise<Contact>;
list(params?: ListContactsRequest): Promise<ListContactsResponse>;
get(id: string): Promise<Contact>;
update(id: string, data: UpdateContactRequest): Promise<Contact>;
delete(id: string): Promise<void>;
};
estates: {
create(data: CreatePropertyRequest): Promise<Property>;
list(params?: ListPropertiesRequest): Promise<ListPropertiesResponse>;
get(id: string): Promise<Property>;
update(id: string, data: UpdatePropertyRequest): Promise<Property>;
delete(id: string): Promise<void>;
};
matcher: {
match(data: MatchRequest): Promise<MatchResponse>;
};
agents: {
facebookStrategy(data: FacebookStrategyRequest): Promise<MarketingStrategy>;
compareProperties(data: ComparePropertiesRequest): Promise<Comparison>;
};
documents: {
generateQuote(data: GenerateQuoteRequest): Promise<Quote>;
};
}🆘 Troubleshooting
Common Issues
1. Authentication Errors
// Check API key configuration
if (!process.env.ESTO_API_KEY) {
throw new Error('ESTO_API_KEY environment variable is required');
}
// Verify API key format
if (!process.env.ESTO_API_KEY.startsWith('esto_')) {
throw new Error('Invalid API key format');
}2. Rate Limiting
// Implement exponential backoff
const retryWithBackoff = async (fn, maxRetries = 3) => {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.code === 'RATE_LIMIT' && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
};3. Network Issues
// Add timeout and retry logic
const client = new EstoClient({
apiKey: process.env.ESTO_API_KEY,
baseUrl: process.env.ESTO_API_BASE_URL,
timeout: 30000, // 30 seconds
retries: 3,
});Debug Mode
// Enable debug logging
const client = new EstoClient({
apiKey: process.env.ESTO_API_KEY,
baseUrl: process.env.ESTO_API_BASE_URL,
debug: true,
});
// Debug logs will show:
// - Request/response details
// - Rate limiting information
// - Error details📞 Support
Getting Help
- Documentation: docs.esto.com
- API Reference: api.esto.com
- Support Email: support@esto.com
- Community: community.esto.com
SDK Updates
# Check for SDK updates
npm outdated @esto/sdk
# Update SDK
npm update @esto/sdk
# Check changelog
npm view @esto/sdk changelog📚 Next Steps
- API Reference - Complete API documentation
- Data Models - Entity schemas and relationships
- Recommendation Logic - AI matching algorithms
- Testing - Testing strategies