Skip to main content

Custom Integrations

Build powerful custom integrations to connect PropertyBase with your systems.

Integration Patterns​

Data Sync​

Keep data synchronized between systems:

// Sync leads from CRM to PropertyBase
async function syncLeadsFromCRM() {
const crmLeads = await crm.getNewLeads();

for (const lead of crmLeads) {
await propertybase.leads.create({
name: lead.fullName,
email: lead.email,
phone: lead.phone,
source: 'crm_sync',
external_id: lead.id,
});
}
}

// Run every 5 minutes
setInterval(syncLeadsFromCRM, 5 * 60 * 1000);

Event-Driven​

React to events in real-time:

// Process webhook events
app.post('/webhooks/propertybase', async (req, res) => {
const { event, data } = req.body;

switch (event) {
case 'lead.created':
// Notify sales team in Slack
await slack.sendMessage('#sales', `New lead: ${data.name}`);
break;

case 'deal.closed':
// Update accounting system
await accounting.createInvoice(data);
break;
}

res.status(200).send('OK');
});

Batch Processing​

Process large amounts of data:

async function importPropertiesFromCSV(csvPath: string) {
const properties = await parseCSV(csvPath);

// Process in batches of 100
for (let i = 0; i < properties.length; i += 100) {
const batch = properties.slice(i, i + 100);

await propertybase.properties.bulkCreate(batch);

// Respect rate limits
await delay(1000);
}
}

Common Integrations​

CRM Integration​

Sync contacts and deals:

class CRMIntegration {
// Sync PropertyBase leads to CRM
async syncLeadToCRM(lead: Lead) {
const contact = await crm.contacts.upsert({
email: lead.email,
name: lead.name,
phone: lead.phone,
custom_fields: {
propertybase_id: lead.id,
source: lead.source,
},
});

// Create deal in CRM
if (lead.stage !== 'new') {
await crm.deals.create({
contact_id: contact.id,
stage: mapStage(lead.stage),
value: lead.budget?.max,
});
}
}

// Sync CRM updates back to PropertyBase
async handleCRMWebhook(event: CRMEvent) {
if (event.type === 'deal.won') {
const lead = await propertybase.leads.findByExternalId(event.contact_id);
await propertybase.leads.markAsWon(lead.id, {
deal_value: event.value,
});
}
}
}

Marketing Automation​

Connect with email marketing:

class MarketingIntegration {
// Add leads to email lists
async addLeadToList(lead: Lead) {
await mailchimp.lists.addMember(LIST_ID, {
email_address: lead.email,
merge_fields: {
FNAME: lead.name.split(' ')[0],
LNAME: lead.name.split(' ').slice(1).join(' '),
SOURCE: lead.source,
},
tags: this.mapTags(lead),
});
}

// Trigger automations based on stage
async handleStageChange(lead: Lead, newStage: string) {
if (newStage === 'viewing') {
await mailchimp.automations.trigger('viewing-reminder', lead.email);
}
}
}

Accounting Integration​

Sync financial data:

class AccountingIntegration {
// Create invoice when deal closes
async createInvoice(deal: Deal) {
const invoice = await quickbooks.invoices.create({
customer_ref: deal.client_email,
line_items: [{
description: `Commission for ${deal.property_name}`,
amount: deal.commission,
}],
});

// Update PropertyBase with invoice reference
await propertybase.deals.update(deal.id, {
custom_fields: { invoice_id: invoice.id },
});
}
}

Portal Syndication​

Publish to property portals:

class PortalSyndication {
async publishProperty(property: Property) {
// Format for portal
const listing = {
reference: property.id,
title: property.name,
description: property.description,
price: property.pricing.min_price,
currency: property.pricing.currency,
images: property.media.gallery,
location: property.location,
};

// Publish to multiple portals
await Promise.all([
rightmove.publish(listing),
zoopla.publish(listing),
onthemarket.publish(listing),
]);
}

// Handle inquiries from portals
async handlePortalInquiry(inquiry: PortalInquiry) {
const lead = await propertybase.leads.create({
name: inquiry.name,
email: inquiry.email,
phone: inquiry.phone,
source: `portal_${inquiry.portal}`,
interested_properties: [inquiry.property_id],
notes: inquiry.message,
});

return lead;
}
}

Data Mapping​

Field Mapping​

Map fields between systems:

const fieldMapping = {
// PropertyBase -> External
toExternal: {
'name': 'full_name',
'email': 'email_address',
'phone': 'mobile_phone',
'stage': (stage) => mapStage(stage),
'budget.max': 'budget_amount',
},

// External -> PropertyBase
toPropertyBase: {
'full_name': 'name',
'email_address': 'email',
'mobile_phone': 'phone',
'deal_stage': (stage) => mapStageReverse(stage),
'budget_amount': 'budget.max',
},
};

function transform(data: object, mapping: object) {
const result = {};

for (const [from, to] of Object.entries(mapping)) {
const value = get(data, from);
if (typeof to === 'function') {
set(result, from, to(value));
} else {
set(result, to, value);
}
}

return result;
}

Error Handling​

Retry Logic​

async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3,
delay = 1000
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;

// Exponential backoff
await sleep(delay * Math.pow(2, i));
}
}
}

// Usage
await withRetry(() => propertybase.leads.create(lead));

Dead Letter Queue​

Handle failed events:

async function processWebhook(event: WebhookEvent) {
try {
await handleEvent(event);
} catch (error) {
// Store failed event for retry
await deadLetterQueue.add({
event,
error: error.message,
timestamp: new Date(),
retryCount: 0,
});
}
}

// Retry failed events
async function retryFailedEvents() {
const failed = await deadLetterQueue.getAll();

for (const item of failed) {
if (item.retryCount < 5) {
try {
await handleEvent(item.event);
await deadLetterQueue.remove(item.id);
} catch {
await deadLetterQueue.update(item.id, {
retryCount: item.retryCount + 1,
});
}
}
}
}

Testing​

Mock API Responses​

import nock from 'nock';

describe('PropertyBase Integration', () => {
beforeEach(() => {
nock('https://api.propertybase.ai')
.get('/v1/leads/lead_123')
.reply(200, {
success: true,
data: { id: 'lead_123', name: 'Test Lead' },
});
});

it('should fetch lead details', async () => {
const lead = await propertybase.leads.get('lead_123');
expect(lead.name).toBe('Test Lead');
});
});

Deployment​

Environment Variables​

PROPERTYBASE_API_KEY=pb_live_xxxxx
PROPERTYBASE_WEBHOOK_SECRET=whsec_xxxxx
PROPERTYBASE_ENV=production

Health Checks​

app.get('/health', async (req, res) => {
const checks = {
propertybase: await checkPropertyBase(),
database: await checkDatabase(),
external_api: await checkExternalAPI(),
};

const healthy = Object.values(checks).every(Boolean);
res.status(healthy ? 200 : 503).json({ healthy, checks });
});