Skip to main content

Building Custom Integrations

When the pre-built integrations do not cover your needs, build a custom integration using the SalesOS REST API and webhooks. This guide covers architecture patterns, best practices, and common scenarios.

Integration Patterns

One-Way Sync (SalesOS to External)

Push data from SalesOS to another system:
  1. Set up webhooks for the events you care about (e.g., deal.won, lead.created)
  2. Build a receiver that processes webhook payloads
  3. Transform and push the data to your target system
Best for: Sending deal data to your ERP, syncing leads to marketing tools, posting notifications to chat systems.

One-Way Sync (External to SalesOS)

Pull or push data from an external system into SalesOS:
  1. Listen for events in your external system (or poll on a schedule)
  2. Transform the data to match SalesOS field formats
  3. Call the SalesOS API to create or update records
Best for: Importing leads from web forms, syncing contacts from an external CRM, creating deals from e-commerce orders.

Two-Way Sync

Keep data synchronized between SalesOS and another system:
  1. Webhook from SalesOS updates the external system
  2. Webhook or poll from external updates SalesOS
  3. Conflict resolution handles simultaneous changes
Two-way syncs can create infinite loops if not carefully designed. Always include a mechanism to detect and skip updates that originated from the other system (e.g., using a sync marker field or comparing timestamps).

Scheduled Batch Sync

For systems that do not support real-time events:
  1. Run on a schedule (e.g., every hour via cron or n8n)
  2. Fetch changes since last sync using updated_after filters
  3. Process changes in batches
  4. Track sync state (last sync timestamp, page cursors)

Building Your Integration

Step 1: Plan Your Data Flow

Before writing code, document:
  • Which SalesOS entities you need (leads, deals, users)
  • Which fields map between systems
  • Which direction data flows (one-way or two-way)
  • How often data needs to sync (real-time, hourly, daily)
  • How to handle conflicts and errors

Step 2: Set Up Authentication

Create an API client with the minimum required scopes:
curl -X POST "https://auth.play2sell.com/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "audience": "https://api.play2sell.com",
    "grant_type": "client_credentials"
  }'
Store the token and handle refresh logic. See the Authentication guide.

Step 3: Implement the Integration

const axios = require('axios');

const salesos = axios.create({
  baseURL: 'https://api.play2sell.com/v1',
  headers: {
    'Authorization': `Bearer ${process.env.SALESOS_TOKEN}`,
    'Content-Type': 'application/json'
  }
});

// Create a lead from an external source
async function syncLeadToSalesOS(externalLead) {
  try {
    const response = await salesos.post('/leads', {
      name: externalLead.fullName,
      email: externalLead.email,
      phone: externalLead.phone,
      company: externalLead.company,
      source: 'external_crm',
      custom_fields: {
        external_id: externalLead.id
      }
    });
    return response.data;
  } catch (error) {
    if (error.response?.status === 409) {
      console.log('Lead already exists, updating...');
      // Handle duplicate
    }
    throw error;
  }
}

Step 4: Handle Errors

Your integration should handle:
  • Rate limiting — Respect 429 responses and back off using the Retry-After header
  • Validation errors — Log 422 responses and fix data mapping issues
  • Network failures — Implement retry logic with exponential backoff
  • Duplicate detection — Handle 409 Conflict responses gracefully

Step 5: Monitor and Maintain

  • Log all API calls and responses for debugging
  • Set up alerts for integration failures
  • Monitor API usage against rate limits
  • Keep your token refresh mechanism working
  • Test after SalesOS updates

Webhook Receiver Example

A minimal webhook receiver in Node.js (Express):
const express = require('express');
const crypto = require('crypto');
const app = express();

app.post('/webhooks/salesos', express.json(), (req, res) => {
  // Verify signature
  const signature = req.headers['x-salesos-signature'];
  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  // Process the event
  const { type, data } = req.body;
  console.log(`Received event: ${type}`, data);

  // Respond quickly, process async
  res.status(200).send('OK');

  // Handle the event asynchronously
  processEvent(type, data).catch(console.error);
});

app.listen(3000);
Always respond to webhooks quickly (within 10 seconds) and process the payload asynchronously. This prevents timeouts and retries.

Best Practices

  • Use idempotent operations — Your integration should produce the same result if the same event is processed twice
  • Map external IDs — Store the external system’s ID in a SalesOS custom field to enable lookups and deduplication
  • Log everything — Comprehensive logging makes debugging much easier
  • Test with staging — Use the SalesOS staging environment to test before going to production
  • Handle partial failures — In batch operations, do not let one failure stop the entire batch

Next Steps