sync
Read when: running continuous capture, one-shot sync, contact/group refresh, or background media download.
wacli sync requires an existing authenticated store and never displays a QR code. It captures WhatsApp Web events into the local SQLite store.
#Command
wacli sync [--once] [--follow] [--idle-exit 30s] [--max-reconnect 5m] [--stale-threshold DURATION] [--presence-mode normal|quiet] [--send-spacing DURATION|MIN-MAX] [--max-messages N] [--max-db-size SIZE] [--download-media] [--refresh-contacts] [--refresh-groups] [--refresh-channels] [--events] [--webhook URL] [--webhook-secret SECRET] [--webhook-events LIST]
#Modes
- Default behavior follows continuously.
--onceexits after sync becomes idle.--idle-exitcontrols idle exit timing in once mode.--max-reconnect 0keeps reconnecting indefinitely.- If WhatsApp revokes the linked session, sync emits a terminal
logged_outevent, cancels any reconnect already in progress, and exits cleanly. Re-pair withwacli auth logoutfollowed bywacli auth --phone. --max-messages Nstops before storing more thanNtotal messages locally.--max-db-size SIZEstops whenwacli.dbplus SQLite sidecars reachesSIZE(500MB,2GB, etc.).--download-mediaruns a bounded media downloader for sync events. Clean one-shot and bootstrap runs finish queued downloads before exiting; cancellation, errors, and storage-limit exits stop immediately.--send-spacing DURATION|MIN-MAXpaces serialized sends delegated to a running follow process. A single duration such as2ssets a fixed minimum gap; a range such as500ms-5schooses a fresh random gap for each send. It is disabled by default, so unset behavior remains unchanged. The caller's command timeout includes time queued behind earlier sends, pacing, and the send itself; a request that runs out of time is not dispatched.--refresh-contactsimports contacts from the session store.--refresh-groupsfetches joined groups live and updates the local DB.--refresh-channelsfetches subscribed WhatsApp Channels live and updates local chat rows.--webhook URLposts successfully stored live message events as JSON on a bounded background worker. The payload includesChatNamewhen a locally resolved chat name is available.--webhook-secret SECRETsigns webhook payloads withX-Wacli-Signature: sha256=<hmac>.--webhook-events LISTselects which event types are posted, as a comma-separated list ofmessage,receipt, andchat_presence. The default ismessage, which posts exactly what earlier versions posted. A list that omitsmessagestops message posts, so--webhook-events receiptposts receipts only.chat_presenceneeds--presence-mode normal(the default): WhatsApp only sends typing notifications to devices that mark themselves available. See Webhook payloads.- Webhook delivery is best-effort: failures, request timeouts, and full-queue drops are logged as warnings and do not stop sync. Retries/backoff are intentionally out of scope for this flag.
- If neither storage cap is configured, sync prints one warning because WhatsApp history can grow the local database substantially.
WACLI_SYNC_MAX_MESSAGESandWACLI_SYNC_MAX_DB_SIZEapply the same caps toauthbootstrap sync andsync.- While
sync --followis running,send text,send file,send sticker,send voice,send react, andmessages editcommands for the same store are delegated to the running sync process so they do not fail on the store lock. - After connecting, sync fetches WhatsApp chat app-state deltas (
regular_highandregular_low) so starred, delete-for-me, mute, archive, pin, and mark-read changes made whilewacliwas offline are caught up instead of relying only on live push notifications. - Sync imports messages sent from your other linked devices into the destination chat with
from_me=true, so local history covers both incoming and outgoing conversation sides. - If whatsmeow reports an app-state LTHash mismatch, sync asks the primary device for the official recovery snapshot once for that app-state collection. If recovery also fails, the warning is printed and sync keeps handling normal message/history events.
- Sync stores WhatsApp call signaling and call-log metadata in
call_events; inspect it withwacli calls list. - Sync stores WhatsApp status broadcasts in
status_messages, separate from normal chatmessages. - In an interactive terminal, routine connected/history/progress updates share one updating stderr status line. Warnings and errors still print as separate lines so they remain visible.
--stale-threshold DURATIONin follow mode detects keepalive failures. If whatsmeow reports that the last successful keepalive is older than this duration, sync force-closes the connection and reconnects. Healthy quiet sessions are not reconnected just because no chat events arrive. Disabled by default (0); accepted values are1sup to but not including2m20s, which reserves one maximum keepalive probe interval plus response deadline before whatsmeow's own 3-minute auto-reconnect window.--presence-mode normal|quietcontrols global linked-device presence during sync.normalis the default and preserves the existing behavior: sync sends available presence after connecting or receiving a push-name update, then sends unavailable presence on cleanup.quietsuppresses the available-presence sends while keeping the final unavailable cleanup; use it for personal-number mirrors where keeping primary-phone notifications audible matters. WhatsApp ultimately controls notification routing, so this mode avoids the active linked-device signal but cannot guarantee phone behavior on every platform.- A
staleNDJSON event is emitted when the threshold is exceeded, containingthreshold,idle_duration,error_count, andsourcefields. - While
sync --followis running, aHEARTBEATfile is written to the store directory (at most once per minute) with the last observed follow activity timestamp in RFC 3339 format. External watchdogs orwacli doctorcan read this as an activity marker; quiet healthy sessions may not update it because successful keepalives are silent, and keepalive health is reported separately throughstaleevents. --eventsemits one NDJSON lifecycle event per stderr line for machine consumers. Routine human progress/status lines, interrupt prompts, and command errors are emitted as events while events are enabled.
#Webhook payloads
Webhook payloads remain flat JSON objects. Receipt and chat-presence payloads carry an EventType discriminator. Message payloads deliberately omit it so existing consumers receive the same signed body as before; a missing EventType means message.
Messages use the stored live message payload documented above, unchanged:
{"Chat":"15551234567@s.whatsapp.net","ID":"3EB0…","SenderJID":"15551234567@s.whatsapp.net","Timestamp":"2026-07-25T10:00:00Z","FromMe":false,"Text":"hi","ChatName":"Alice"}
EventType: "receipt" reports delivery and read state for messages you sent. Only delivered, read, and played cross the webhook; the protocol bookkeeping types (sender, retry, read-self, played-self, inactive, server-error, peer_msg, hist_sync) are dropped at the source so they cannot crowd out real messages. The delivered type is spelled out explicitly, even though WhatsApp sends it as an empty string on the wire. MessageIDs keeps WhatsApp's batching (one POST per receipt, not per message, minus any blank IDs), and in groups Sender is the participant the receipt came from:
{"EventType":"receipt","Chat":"120363000000000000@g.us","Sender":"15551234567@s.whatsapp.net","MessageIDs":["3EB0…"],"Timestamp":"2026-07-25T10:00:01Z","Type":"delivered","IsFromMe":false}
EventType: "chat_presence" reports per-chat typing state. Media is audio while the contact records a voice message and empty otherwise. Global presence (online / last seen) is deliberately not forwarded:
{"EventType":"chat_presence","Chat":"15551234567@s.whatsapp.net","Sender":"15551234567@s.whatsapp.net","State":"composing","Media":""}
#Examples
wacli sync --once
wacli sync --follow --max-reconnect 10m
wacli sync --follow --stale-threshold 2m
wacli sync --follow --presence-mode quiet
wacli sync --follow --send-spacing 500ms-5s
wacli sync --follow --max-messages 250000 --max-db-size 2GB
wacli sync --once --refresh-contacts --refresh-groups --refresh-channels
wacli sync --follow --download-media
wacli sync --once --events 2>events.ndjson
wacli sync --follow --stale-threshold 2m --events 2>events.ndjson
wacli sync --follow --webhook https://example.com/wacli --webhook-secret "$WACLI_WEBHOOK_SECRET"
wacli sync --follow --webhook https://example.com/wacli --webhook-events message,receipt,chat_presence