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:6379

3. Install SDK

JavaScript/TypeScript

npm install @esto/sdk
# or
yarn add @esto/sdk

Python

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

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