Your account, fresh — by sub-system.
Data Sync Engine is the plumbing layer behind every Lyra tool. Each data type has its own sync on its own cadence — recommendations hourly, RSA assets daily, negative keywords daily, Slack messages daily. The main campaign-data sync is on-demand. All sub-syncs UPSERT-keyed and idempotent — re-runs never duplicate.
Five sub-syncs, five cadences.
Each data type has its own sync on its own cadence. Recommendations refresh hourly because they change fast; RSA assets refresh daily because they don't. The main campaign-data sync is on-demand because it's heavy. All five UPSERT-keyed by (customer_id, entity_id, date) — idempotent.
- Google Ads API
- Slack
- Shopify
- Merchant Center
- campaign_daily_data
- ad_group_daily_data
- asset_group_daily_data
- search_terms_snapshots
- One sub-sync fails
- Re-run is idempotent
- Stale-data warning
Fresh by sub-system , not all at once.
Top strip: per-source freshness across the 5 sub-syncs. Below: the last 5 sync runs across an agency portfolio of 60 accounts. Each sub-sync runs independently — recommendations refresh fast, RSA refreshes daily, main campaign data only when triggered.
diary_slack_sync_state; re-run is idempotent (Slack ts is the dedup key). Per source, per cadence , per table.
There isn't one big sync. There are five, each tuned to its data type. The pattern is the same for all five: read from source, normalise, UPSERT into the destination table on a composite key. Idempotent by design.
The fastest-changing surface.
Google Ads recommendations refresh hourly because they're the surface most actively used by automation (Auto-Apply Guard, the recommendations feed UI). Pulls every active rec across every connected account; UPSERT-keyed by (customer_id, recommendation_id, fetched_at).
Performance labels for ads.
Per-RSA headline + description performance labels — Google's POOR / AVERAGE / GOOD / EXCELLENT. Used by PMax Asset Diagnostics. Daily is sufficient because labels don't shift hour-to-hour.
Existing negatives, mirrored.
Pulls every account's existing negative-keyword lists (campaign + ad-group level). Used to filter Search Terms Management — terms already negated don't appear as new candidates. Daily is fine because negative lists don't churn.
Channel messages → diary.
Configured Slack channels per client; each day's run captures messages with `source_user_name` set to the Slack author. Per-channel cursor in `diary_slack_sync_state` makes re-runs idempotent (Slack ts is the dedup key).
Heavy, so you trigger it.
The main campaign-data sync (`/sync/yesterday`) is heavy — it pulls daily metrics for every campaign, ad group, and asset group across every connected account. Triggered on-demand (typically nightly via an external orchestrator) rather than scheduled inside Lyra's own cron list.
Same key, same row.
Every sub-sync writes via INSERT … ON CONFLICT (customer_id, entity_id, date) DO UPDATE. Re-running a failed sync lands on the same rows — no duplicates, no orphans, no drift between runs. The sync's correctness contract.
The honest answers about syncs.
What's on a cron and what's on-demand?
Recommendations sync runs hourly. RSA assets daily at 02:00 UTC. Negative keywords daily at 01:00 UTC. Slack messages daily at 03:45 UTC. The main campaign-data sync (`/sync/yesterday`) is on-demand — usually triggered nightly by an external orchestrator, not by Lyra's own internal cron today. The freshness dashboard surfaces which sub-syncs are stale at any point.
Why isn't the main sync on a cron?
Historical reason: the main sync is heavy (60+ accounts, four tables, daily metric per entity), and the orchestrator that triggers it lives upstream of Lyra in this deployment. Adding an internal cron for it is on the deferred backlog. Sub-syncs are on internal crons because they're scoped, fast, and tied to specific in-product features.
What if a sync run fails?
Each sub-sync is independent — one sub-sync's failure doesn't block the others. Because every sync UPSERTs on a composite key, re-running a failed sub-sync is safe (no duplicates). Failed sub-syncs surface as a 'stale' status in the freshness dashboard until the next successful run lands.
Is there an LLM in the sync path?
No. Pure ETL. The sync engine reads from sources (Google Ads API, Slack, Shopify, Merchant Center), normalises the response, and UPSERTs into the destination table. Lyra Agent and the rest of the AI layer read from the synced tables; they don't participate in the sync itself.
Can I trigger a manual sync?
Yes. `POST /sync/yesterday` triggers the main campaign-data sync for one or all connected accounts. Sub-syncs can be triggered manually too (each has its own endpoint), though the cron usually keeps them fresh enough that you rarely need to.
Run the audit.
Keep the findings.
Connect your account and let Lyra run its 18 tools for 14 days. If the projected waste recovery isn't worth at least 10× the $49, don't pay. You keep every insight either way.
Read-only connect · write access opt-in per tool · SOC 2 in progress · GDPR + CCPA compliant