Skip to content

Periodic Trunk Sync ​

Overview ​

The trunk sync service runs a bidirectional sync between MongoDB and LiveKit every minute via a cron job. MongoDB is the single source of truth. The periodic sync ensures LiveKit state always matches the database, replacing the previous reactive (event-driven) approach.

Sync Schedule ​

TriggerFrequencyScope
Server startupOnceAll trunks
Cron jobEvery 1 minuteAll trunks
Manual API callOn demandAll trunks

Manual Trigger ​

POST /telephony/superadmin/sip-trunks/sync

Requires superadmin role. Calls the same syncAllTrunks() function.

Sync Behavior ​

Each sync run performs two phases sequentially:

Phase 1: Outbound Trunks ​

  1. Fetch all active outbound trunks from MongoDB
  2. Fetch all outbound trunks from LiveKit
  3. For each DB trunk:
    • If it exists on LiveKit → check for config drift (name, address, numbers). If drifted → update LiveKit to match DB
    • If it's missing on LiveKit → recreate it and store the new livekitTrunkId in MongoDB
  4. For each LiveKit trunk not referenced by any DB document → delete it (orphan cleanup)
  5. When a trunk is recreated with a new LiveKit ID → update Redis campaign configs for any running campaigns using that trunk

Phase 2: Inbound Trunks ​

  1. Fetch all active inbound trunks from MongoDB
  2. Fetch all inbound trunks and dispatch rules from LiveKit
  3. For each DB trunk:
    • If it exists on LiveKit → check for config drift. If drifted → update LiveKit
    • If its dispatch rule is missing → recreate the dispatch rule
    • If it's missing on LiveKit → recreate trunk + dispatch rule, update MongoDB
  4. Orphan cleanup: delete any LiveKit inbound trunks or dispatch rules not referenced by DB

Redis Campaign Config Update ​

When an outbound trunk is recreated (new LiveKit ID), the sync also updates Redis for running campaigns:

  • Finds all campaigns with isRunning: true
  • For each, checks campaign:{uid}:config → trunkUID field
  • If it matches the recreated trunk's trunkUID → updates sipTrunkId with the new LiveKit ID

This ensures active campaigns pick up the new trunk ID on their next call dispatch without requiring a campaign restart.

Overlap Protection ​

A simple in-memory lock (isSyncing flag) prevents concurrent sync executions. If the cron fires while a previous sync is still in progress, the new invocation returns immediately with a "skipped" status.

Config Drift Detection ​

Outbound Trunks ​

A drift is detected if any of these differ between DB and LiveKit:

  • Trunk name
  • SIP address
  • Phone numbers (order-independent comparison)

Inbound Trunks ​

A drift is detected if any of these differ:

  • Trunk name
  • Phone numbers (order-independent comparison)

Agent Behavior on "Trunk Not Found" ​

When the Python agent encounters a "trunk does not exist" error from LiveKit during a call attempt:

  1. The agent logs the error
  2. Sets disconnect reason to trunk_not_found
  3. Deletes the room (fails gracefully)
  4. Does not retry or call any sync endpoint

The periodic cron (within the next 60 seconds) will recreate the missing trunk on LiveKit. The campaign retry mechanism will then re-attempt the contact on a subsequent cycle.

Logging ​

ConditionLog Output
No changes detected🔄 Trunk sync (cron): all in sync (outbound: N, inbound: M)
Changes detected🔄 Trunk sync (cron): changes detected + full report
Sync skipped (overlap)No log (silent skip)
Sync error❌ Trunk sync cron error: ...

Files ​

FileRole
services/trunkSync.service.tsCore sync logic (syncAll, outbound/inbound phases, Redis update)
services/scheduler.service.tsCron registration (every minute)
services/livekit.service.tsLiveKit SDK wrappers (create, update, delete, list)

Last updated: