Sync Execution Engine

The sync execution engine is responsible for moving data between Otesse and connected third-party providers. It supports inbound sync (provider to Otesse), outbound sync (Otesse to provider), and bidirectional sync with configurable conflict resolution. Each sync run is tracked as a SyncExecution record with detailed counts of processed, created, updated, skipped, and failed records.

Sync Jobs

A sync job defines what data to sync, in which direction, and on what schedule. Each job is tied to a specific integration connection and entity type.

Creating a Sync Job

From the connected app detail view, click "Add Sync Job" and configure:

FieldDescriptionOptions
NameDescriptive name for the jobFree text (e.g., "Stripe Customer Sync")
Entity TypeWhat kind of data to syncDepends on provider (customer, invoice, product, etc.)
DirectionWhich way data flowsInbound, Outbound, or Bidirectional
ModeFull sync or incrementalFull (all records) or Incremental (changed since last run)
ScheduleWhen the job runsManual only, or a cron schedule
Retry on FailureWhether to automatically retryYes/No (default: Yes)
Max RetriesMaximum retry attempts1-10 (default: 3)
TimeoutMaximum execution time1-1440 minutes (default: 60)

Sync Directions

DirectionBehavior
InboundReads data from the provider and creates/updates records in Otesse
OutboundReads records from Otesse and creates/updates records at the provider
BidirectionalSyncs in both directions. Requires a conflict resolution strategy

Sync Modes

ModeBehaviorBest For
FullProcesses all records regardless of modification dateInitial sync, reconciliation after errors
IncrementalOnly processes records modified since the last successful runRegular scheduled syncs, efficiency

Execution Flow

When a sync job runs (manually, on schedule, or triggered by a webhook), the engine executes the following steps:

1. Create Execution Record

A SyncExecution record is created with status "running" and all counters initialized to zero. This record tracks the progress and outcome of the run.

2. Fetch Source Records

For inbound sync:

  • Decrypt stored credentials
  • Call the provider's API to list records of the configured entity type
  • Apply incremental filter if mode is incremental (e.g., ?updated_since={lastRunAt})
  • Page through results using the provider's pagination (default page size: 100)

For outbound sync:

  • Query the local Otesse database for records of the configured entity type
  • Apply incremental filter: records modified since lastRunAt
  • Batch records (default batch size: 100)

3. Process Each Record

For each source record in the batch:

  1. Look up external reference — Check if an ExternalReference exists linking this entity to the provider. If found, this is an update. If not, this is a create.
  1. Apply field mappings — For each FieldMappingRule, read the source field, apply any transformation, apply default values for null fields, and write to the target field.
  1. Detect conflicts (bidirectional only) — If both sides modified the same record since the last sync, apply the configured conflict resolution strategy.
  1. Execute the write — Create or update the target record. For inbound creates, also create an ExternalReference linking the local and provider records.
  1. Increment counters — Track created, updated, skipped, and failed counts.

4. Complete Execution

After all records are processed:

  • Set the execution status based on results
  • Record completedAt and durationSeconds
  • Update the sync job's lastRunAt and calculate nextRunAt (if scheduled)

Execution Status Resolution

ConditionStatusMeaning
Zero failurescompletedAll records processed successfully
Some failures, some successescompletedwitherrorsPartial success
All records failedfailedComplete failure
Circuit breaker triggeredfailedHalted due to >50% failure rate (after 10+ records)
Timeout exceededtimed_outExecution exceeded the configured timeout
User or system cancelledcancelledManually stopped or connection lost

Trigger Types

Sync jobs can be triggered three ways:

TriggerHow It Works
ManualUser clicks "Run Now" on the sync job. Executes immediately.
ScheduledCron scheduler fires at the configured interval (e.g., every 15 minutes, daily at midnight).
WebhookAn inbound webhook event matches the sync job's entity type. Only the affected record is synced (single-record sync for near-real-time updates).

Circuit Breaker

The engine includes a circuit breaker to prevent runaway failures:

  • If more than 50% of processed records fail AND at least 10 records have been processed, the execution is halted early
  • The status is set to "failed" with the circuit breaker reason noted
  • This prevents a bad API key or schema mismatch from generating thousands of error records

Monitoring Sync Jobs

The sync job detail view shows:

SectionContent
ConfigurationName, entity type, direction, mode, schedule, retry settings
Execution HistoryTable of past runs with status, duration, record counts, and trigger type
Execution DetailExpandable view with error logs, record breakdown pie chart, and individual failure details
Dead Letter QueueRecords that have failed across multiple execution attempts (failed 3+ times)