-
Notifications
You must be signed in to change notification settings - Fork 5.5k
feat(label): add GET /label/syncLabels endpoint to force WhatsApp label re-sync #2421
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(label): add GET /label/syncLabels endpoint to force WhatsApp label re-sync #2421
Conversation
Delegates to service.syncLabels() via waMonitor, following the same pattern as fetchLabels and handleLabel.
New endpoint that forces WhatsApp to re-download labels via Baileys resyncAppState(['regular'], true) before returning updated labels from DB. Uses same dataValidate pattern as findLabels.
Uses Baileys client.resyncAppState(['regular'], true) to force an incremental re-download of WhatsApp app state (labels). Waits 3s for LABELS_EDIT/LABELS_ASSOCIATION event handlers to process, then returns updated labels from DB via fetchLabels(). Key: isLatest=true means incremental (safe, no disconnect). isLatest=false would download full snapshot and cause disconnection.
Throws BadRequestException as syncLabels is only available for Baileys (WhatsApp Web) connections.
Throws BadRequestException as syncLabels is only available for Baileys (WhatsApp Web) connections.
Reviewer's GuideAdds a new GET /label/syncLabels/:instanceName endpoint that triggers a Baileys incremental app state resync for labels, waits for label events to be processed, and then returns the refreshed labels from the database, while stubbing the same method as unsupported on non-Baileys channels. Sequence diagram for GET /label/syncLabels endpoint flowsequenceDiagram
actor CRM
participant ApiServer
participant LabelRouter
participant LabelController
participant WaMonitor
participant BaileysStartupService
participant BaileysClient
participant LabelEventHandlers
participant Database
CRM->>ApiServer: HTTP GET /label/syncLabels/:instanceName
ApiServer->>LabelRouter: route syncLabels
LabelRouter->>LabelRouter: dataValidate request to LabelDto
LabelRouter->>LabelController: syncLabels(InstanceDto)
LabelController->>WaMonitor: waInstances[instanceName]
WaMonitor->>BaileysStartupService: syncLabels()
BaileysStartupService->>BaileysClient: resyncAppState([regular], true)
BaileysClient-->>BaileysStartupService: resync initiated
BaileysClient->>LabelEventHandlers: emit LABELS_EDIT events
BaileysClient->>LabelEventHandlers: emit LABELS_ASSOCIATION events
LabelEventHandlers->>Database: upsert label records
BaileysStartupService->>BaileysStartupService: wait 3000 ms
BaileysStartupService->>Database: fetchLabels()
Database-->>BaileysStartupService: LabelDto[]
BaileysStartupService-->>WaMonitor: LabelDto[]
WaMonitor-->>LabelController: LabelDto[]
LabelController-->>LabelRouter: LabelDto[]
LabelRouter-->>ApiServer: HTTP 200 LabelDto[]
ApiServer-->>CRM: HTTP 200 LabelDto[]
Updated class diagram for label sync across channel servicesclassDiagram
class ChannelStartupService {
}
class BaileysStartupService {
+fetchLabels() Promise~LabelDto[]~
+syncLabels() Promise~LabelDto[]~
+handleLabel(data HandleLabelDto) Promise~any~
}
class EvolutionStartupService {
+fetchLabels() Promise~void~
+syncLabels() Promise~void~
+handleLabel() Promise~void~
}
class BusinessStartupService {
+fetchLabels() Promise~void~
+syncLabels() Promise~void~
+handleLabel() Promise~void~
}
class LabelController {
+fetchLabels(instance InstanceDto) Promise~LabelDto[]~
+handleLabel(instance InstanceDto, data HandleLabelDto) Promise~any~
+syncLabels(instance InstanceDto) Promise~LabelDto[]~
}
class LabelRouter {
+router Router
+registerRoutes() void
}
class WaMonitor {
+waInstances map~string, ChannelStartupService~
}
ChannelStartupService <|-- BaileysStartupService
ChannelStartupService <|-- EvolutionStartupService
ChannelStartupService <|-- BusinessStartupService
WaMonitor "1" --> "*" ChannelStartupService : waInstances
LabelController --> WaMonitor : uses
LabelRouter --> LabelController : uses
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey - I've left some high level feedback:
- The hard-coded 3-second timeout in
syncLabelsmay be brittle under varying load/network conditions; consider making this delay configurable or driven by a more deterministic signal that label events have been processed. - In
syncLabels, you currently assumethis.client.resyncAppStatealways succeeds; consider wrapping the call and the wait in a try/catch and returning a clearer error if the resync fails or times out.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The hard-coded 3-second timeout in `syncLabels` may be brittle under varying load/network conditions; consider making this delay configurable or driven by a more deterministic signal that label events have been processed.
- In `syncLabels`, you currently assume `this.client.resyncAppState` always succeeds; consider wrapping the call and the wait in a try/catch and returning a clearer error if the resync fails or times out.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds an API endpoint to force a WhatsApp label re-sync (via Baileys app-state resync) so that labels changed on-device after the initial connection are reflected in the Prisma-backed findLabels response.
Changes:
- Added
GET /label/syncLabels/:instanceNameroute and controller wiring. - Implemented Baileys
syncLabels()usingresyncAppState(['regular'], true)+ delayed DB read. - Added
syncLabels()stubs for non-Baileys channel implementations (Business API / Evolution Channel).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/api/routes/label.router.ts | Registers syncLabels route alongside existing label endpoints |
| src/api/controllers/label.controller.ts | Adds controller delegation to instance syncLabels() |
| src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts | Implements Baileys label re-sync + returns updated labels from DB |
| src/api/integrations/channel/evolution/evolution.channel.service.ts | Adds syncLabels() stub throwing BadRequestException |
| src/api/integrations/channel/meta/whatsapp.business.service.ts | Adds syncLabels() stub throwing BadRequestException |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ClassRef: LabelDto, | ||
| execute: (instance) => labelController.syncLabels(instance), | ||
| }); | ||
|
|
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GET /label/syncLabels/:instanceName triggers a server-side re-sync with WhatsApp (side effects + external I/O). Using GET for a mutating/side-effecting operation breaks HTTP semantics and increases the risk of accidental invocation via caching/prefetching. Consider changing this to POST (or at least add Cache-Control: no-store / disable caching) to make the intent explicit and safer for intermediaries.
| res.set('Cache-Control', 'no-store'); |
| @@ -1,4 +1,4 @@ | |||
| import { getCollectionsDto } from '@api/dto/business.dto'; | |||
| import { getCollectionsDto } from '@api/dto/business.dto'; | |||
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The first import line contains a UTF-8 BOM/zero-width character (shown as an invisible character before import). This can cause noisy diffs and occasional tooling/lint issues. Please remove the BOM so the file starts with a plain import ....
| import { getCollectionsDto } from '@api/dto/business.dto'; | |
| import { getCollectionsDto } from '@api/dto/business.dto'; |
📋 Description
Add
GET /label/syncLabels/:instanceNameendpoint that forces WhatsApp to re-download labels via Baileys'resyncAppState(['regular'], true).Problem
Currently,
GET /label/findLabels/:instanceNameonly reads labels from the Prisma database. Labels created, renamed, or recolored on WhatsApp after the initial connection are never reflected in the API response. There is no way to force a re-sync of labels from WhatsApp.Solution
New endpoint
syncLabelsthat:this.client.resyncAppState(['regular'], true)to trigger an incremental re-download of WhatsApp's label app stateLABELS_EDITandLABELS_ASSOCIATIONevent handlers to process incoming dataLabelDto[]format asfindLabels)Key Technical Details
resyncAppState(['regular'], true)— thetrueparameter means "incremental" (only fetch changes since last sync). This is safe and does NOT cause disconnects.resyncAppState(['regular'], false)— would download the FULL snapshot, which causes WhatsApp to disconnect the session. This was tested and confirmed in production. We intentionally usetrue.labelHandle[Events.LABELS_EDIT]andlabelHandle[Events.LABELS_ASSOCIATION]) to process and persist the incoming label data before reading from DB.findLabels, this is a simple GET with only theinstanceNameparameter.Use Case
CRM integrations (Odoo, Chatwoot, etc.) that need to keep label data in sync with WhatsApp. The CRM calls
syncLabelsperiodically to ensure it has the latest labels before mapping them to contacts/conversations.Files Changed
whatsapp.baileys.service.tssyncLabels()method usingresyncAppState+fetchLabels()label.controller.tssyncLabels()delegation (same pattern asfetchLabels)label.router.tsGET /label/syncLabels/:instanceNameroute (same pattern asfindLabels)evolution.channel.service.tsBadRequestExceptionwhatsapp.business.service.tsBadRequestException🔗 Related Issue
This addresses a gap in the label management API. Related to #1904 (labels.association handling).
🧪 Type of Change
🧪 Testing
Test results (production VPS, Evolution API v2.2.0):
GET /label/syncLabels/testInstance→ HTTP 200, returns updatedLabelDto[]isLatest=true)findLabelsandhandleLabelendpoints completely unaffected✅ Checklist
📝 Additional Notes
resyncAppStatefunction is provided by Baileys but was never used anywhere in the Evolution API codebase before this PR.Summary by Sourcery
Add support for explicitly re-syncing WhatsApp labels and expose it via a new HTTP endpoint.
New Features:
Chores: