How it works
A sync task is a SQL query plus a schedule. When the schedule fires, SuprSend runs the query, adds or replaces the list membership from the result, and — if you enable it — creates any users who don’t yet exist and updates each user’s profile properties and channels from other columns your query returns.Setting up the sync
1. Create the list
Go to Lists → + New List and set alist_id (required — unique within the workspace; lowercase letters, digits, and underscores only), plus an optional name and description.
2. Write the SQL query
Open the list from the Lists page. On the User Sync Query tab, click Setup Sync to launch the query editor.
Empty User Sync Query tab — click Setup Sync to start.

Query editor with connection selector, SQL input, and preview panel.
-
Return
distinct_id— yourSELECTmust include this column; it’s how SuprSend identifies each user. Any additional columns can be mapped to a user property or channel in the next step. - Preview, then save — click Run to preview the first 10 rows and confirm the query pulls what you expect, then click Save Query to persist the draft.
-
Select only what you need — avoid
SELECT *in the committed query. The preview limits to 10 rows, but the live sync would return all rows and columns and can take longer to run. -
Mind column-name case — mappings are case-sensitive, so
Email,email, andEMAILare three different columns. Pick one form and stick with it. -
Format channel columns - make sure that you format the channel values correctly otherwise they’d be dropped while syncing in user profile.
To prepend a country code inside SQL:
- PostgreSQL —
SELECT '+91' || phone_number AS phone FROM users - MySQL —
SELECT CONCAT('+91', phone_number) AS phone FROM users
- PostgreSQL —
Incremental syncs with {{last_sync_time}}
For recurring syncs, use the {{last_sync_time}} template variable to fetch only rows that changed since the previous successful run. This keeps queries fast and avoids re-processing the same rows on every sync.
{{last_sync_time}} is an ISO 8601 timestamp. Add a WHERE clause against an indexed “last updated” column in your source table, and cast the variable if your column uses a different format.
3. Configure the sync settings
Once the preview looks right, click Update Sync Settings and Commit.Choose the update type

Step 1 — choose Add or Replace for how the list membership is updated.
Choose the update frequency

Step 2 — pick One-time or Recurring, then set the interval.
Map columns to user profile
{{$recipient.<property_key>}}.
Step 3 — map query output columns to user properties and channels.
Commit changes

Step 4 — describe what changed in this version and commit.
Enable or disable a sync
A sync is enabled the moment you commit it. Toggle it off from the switch next to the edit button — the query stops running but the sync configuration stays intact, so you can turn it back on later.
Run a sync on demand
Click Sync Now in the user sync task header to trigger an immediate run outside the scheduled interval — useful for testing a new query, backfilling after fixing a source-data issue, or refreshing the list before a broadcast. The manual run uses the currently committed query and settings, honours{{last_sync_time}} from the previous successful run, and appears in Sync Logs like any other run.
Sync logs
Every run appears on the Sync Logs tab of the list. Each row shows when the sync was queued, the source task, who last edited it, the status, and a summary withTotal and Added counts.

Total vs. Added
- Total — the number of users the query returned in this run.
- Added — the number of users actually added to the list after the run finished.
Added can be lower than Total for two reasons:
- The user is already in the list. Duplicate
distinct_ids from earlier runs aren’t added again, so they count towardTotalbut notAdded. - The user doesn’t exist in SuprSend and user sync is off. When Create / Update user profile is off in sync settings, rows whose
distinct_iddoesn’t already exist in SuprSend are skipped. Turn it on to auto-create those users soAddedmatchesTotal.

Version control
Every change to the query or settings is saved as a draft first. Only Commit changes publishes it as the live version — the running sync keeps using the previous version until then. Browse and roll back to older versions from the version history.
Troubleshooting
Sync failed: 'distinct_id column is missing'
Sync failed: 'distinct_id column is missing'
distinct_id (lowercase). If your source uses a different name, alias it: SELECT user_id AS distinct_id FROM users.Sync failed: empty result set
Sync failed: empty result set
WHERE clause — a common cause is a {{last_sync_time}} filter that’s too restrictive on the first run, when there is no previous sync time. Run the preview to confirm the query returns data.Users showed up in the list, but their properties didn't update
Users showed up in the list, but their properties didn't update
Email but the mapping is email, the value won’t be applied. Re-check that the mapping matches the query output exactly.SMS or WhatsApp channels aren't syncing
SMS or WhatsApp channels aren't syncing
+<country-code><number>). If your database stores raw numbers, prepend the country code in the query — CONCAT('+91', phone_number) in MySQL or '+91' || phone_number in PostgreSQL.Runs are being marked 'Ignored'
Runs are being marked 'Ignored'
{{last_sync_time}} filter so each run processes fewer rows.