Skip to main content

External Sync

Alguna’s External Sync enables bidirectional synchronization between Alguna and external systems like CRMs, ERPs, and accounting software. Keep your data in sync automatically with conflict detection and resolution.

How It Works


Supported Systems

SystemEntitiesDirection
SalesforceAccounts, Contacts, OpportunitiesBidirectional
HubSpotCompanies, Contacts, DealsBidirectional
QuickBooksCustomers, Invoices, PaymentsBidirectional
NetSuiteCustomers, Invoices, PaymentsBidirectional
XeroContacts, Invoices, PaymentsBidirectional

Configuration

Enable Sync

curl -X POST https://api.alguna.io/integrations/salesforce/sync-config \
  -H "Authorization: Bearer $ALGUNA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "direction": "bidirectional",
    "entities": ["accounts", "contacts", "subscriptions"],
    "conflictResolution": "alguna_wins",
    "schedule": "*/15 * * * *"
  }'

Sync Direction Options

DirectionDescription
alguna_to_externalPush Alguna changes to external
external_to_algunaPull external changes to Alguna
bidirectionalSync both ways

Field Mapping

Map fields between systems:
await alguna.syncConfig.setFieldMappings({
  integrationId: 'int_salesforce',
  entity: 'accounts',
  mappings: [
    {
      algunaField: 'name',
      externalField: 'Name',
      direction: 'bidirectional',
    },
    {
      algunaField: 'email',
      externalField: 'Email__c',
      direction: 'bidirectional',
    },
    {
      algunaField: 'billingAddress.line1',
      externalField: 'BillingStreet',
      direction: 'alguna_to_external',
    },
    {
      algunaField: 'mrr',
      externalField: 'MRR__c',
      direction: 'alguna_to_external',
      transform: 'number_to_currency',
    },
  ],
});

Transform Functions

TransformDescription
nonePass through as-is
number_to_currencyFormat as currency
date_to_isoConvert to ISO format
boolean_to_stringConvert true/false to Yes/No
customUse custom JavaScript function

Conflict Resolution

When the same record is modified in both systems:

Resolution Strategies

StrategyDescription
alguna_winsAlguna data takes precedence
external_winsExternal data takes precedence
latest_winsMost recent modification wins
manualFlag for manual review

Per-Field Resolution

await alguna.syncConfig.setConflictResolution({
  integrationId: 'int_salesforce',
  entity: 'accounts',
  default: 'latest_wins',
  perField: {
    'name': 'external_wins', // CRM is source of truth for names
    'email': 'manual', // Review email conflicts
    'mrr': 'alguna_wins', // Billing is source of truth for MRR
  },
});

Sync Triggers

Scheduled Sync

Run sync on a schedule:
{
  "schedule": "*/15 * * * *",
  "description": "Every 15 minutes"
}

Real-Time Sync

Trigger sync on changes:
{
  "triggers": {
    "onAlgunaChange": true,
    "onWebhook": true,
    "webhookUrl": "https://api.alguna.io/integrations/sync/trigger"
  }
}

Manual Sync

curl -X POST https://api.alguna.io/integrations/salesforce/sync/trigger \
  -H "Authorization: Bearer $ALGUNA_API_KEY" \
  -d '{
    "entities": ["accounts"],
    "fullSync": false
  }'

Sync Status

Check Sync State

curl https://api.alguna.io/integrations/salesforce/sync/status \
  -H "Authorization: Bearer $ALGUNA_API_KEY"
Response:
{
  "integrationId": "int_salesforce",
  "lastSync": "2024-01-20T10:15:00Z",
  "status": "healthy",
  "stats": {
    "accounts": {
      "synced": 1250,
      "created": 5,
      "updated": 23,
      "conflicts": 2,
      "errors": 0
    },
    "contacts": {
      "synced": 3400,
      "created": 12,
      "updated": 45,
      "conflicts": 0,
      "errors": 1
    }
  },
  "nextScheduledSync": "2024-01-20T10:30:00Z"
}

View Sync History

curl https://api.alguna.io/integrations/salesforce/sync/history \
  -H "Authorization: Bearer $ALGUNA_API_KEY" \
  -d '{"limit": 10}'

Handling Errors

Error Types

ErrorDescriptionResolution
field_validationExternal system rejected field valueCheck field mapping
rate_limitExternal API rate limitedAutomatic retry
auth_expiredAuthentication expiredReconnect integration
not_foundRecord doesn’t existSkip or create

Error Webhooks

app.post('/webhooks/alguna', (req, res) => {
  const event = req.body;

  if (event.type === 'sync.error') {
    console.error('Sync error:', event.data);
    // Alert team, create ticket, etc.
  }

  res.sendStatus(200);
});

Retry Failed Syncs

curl -X POST https://api.alguna.io/integrations/salesforce/sync/retry \
  -H "Authorization: Bearer $ALGUNA_API_KEY" \
  -d '{
    "recordIds": ["acc_123", "acc_456"]
  }'

Entity Matching

By External ID

Link records using external IDs:
await alguna.accounts.create({
  name: 'Acme Corp',
  externalId: 'salesforce:001ABC123', // Salesforce Account ID
});

By Email/Name

Configure matching rules:
{
  "matching": {
    "accounts": {
      "fields": ["email", "name"],
      "strategy": "exact", // or "fuzzy"
      "createIfNotFound": true
    }
  }
}

Salesforce Example

Complete Setup

// 1. Connect Salesforce
const integration = await alguna.integrations.connect({
  provider: 'salesforce',
  credentials: {
    // OAuth flow handles this
  },
});

// 2. Configure sync
await alguna.syncConfig.create({
  integrationId: integration.id,
  entities: {
    accounts: {
      enabled: true,
      externalObject: 'Account',
      direction: 'bidirectional',
      mappings: [
        { alguna: 'name', external: 'Name' },
        { alguna: 'email', external: 'Email__c' },
        { alguna: 'mrr', external: 'MRR__c' },
      ],
    },
    subscriptions: {
      enabled: true,
      externalObject: 'Opportunity',
      direction: 'alguna_to_external',
      mappings: [
        { alguna: 'planId', external: 'Product__c' },
        { alguna: 'status', external: 'Stage' },
        { alguna: 'currentVersion.mrr', external: 'Amount' },
      ],
    },
  },
  schedule: '*/15 * * * *',
});

// 3. Run initial sync
await alguna.integrations.sync.trigger(integration.id, {
  fullSync: true,
});

Best Practices

Start One-Way

Begin with one-way sync, then enable bidirectional once stable.

Monitor Conflicts

Review conflicts regularly to tune resolution strategies.

Test in Sandbox

Test sync configuration in sandbox environments first.

Map Carefully

Document field mappings and transformation logic.

Webhooks

EventDescription
sync.completedSync job finished
sync.errorSync encountered errors
sync.conflictConflict detected (if manual resolution)

Next Steps