[{"data":1,"prerenderedAt":1765},["ShallowReactive",2],{"blog:post:migrating-from-baileys-to-a-hosted-api":3},{"id":4,"title":5,"author":6,"body":7,"date":1747,"description":1748,"extension":1749,"links_to":1750,"meta":1755,"navigation":671,"path":1756,"seo":1757,"stem":1758,"tags":1759,"updated":1763,"__hash__":1764},"blog\u002Fblog\u002Fmigrating-from-baileys-to-a-hosted-api.md","Migrating from Baileys to a Hosted WhatsApp API","Wapito team",{"type":8,"value":9,"toc":1738},"minimark",[10,14,23,28,31,52,66,76,86,92,98,101,105,108,154,158,161,563,566,624,628,631,1593,1599,1603,1610,1640,1644,1647,1657,1674,1680,1701,1707,1713,1716,1720,1723,1734],[11,12,13],"p",{},"Baileys is how a great many WhatsApp integrations start. It is a TypeScript library that speaks\nthe WhatsApp Web protocol directly over a WebSocket, it is MIT-licensed and free, and an evening\nwith it gets you a bot that answers messages. Then the bot goes to production, and the evening\nturns into a job: keeping the socket alive, storing the session, surviving the next protocol\nchange, and explaining to a customer why the number was banned. This post is for the team at\nthat point. It is honest about what a hosted API takes away as well as what it gives, maps every\nBaileys concept you are using onto its Wapito equivalent, and lays out a migration that moves one\nnumber at a time without a big-bang cutover.",[11,15,16,17,22],{},"We run whatsmeow rather than Baileys under the default engine, and we compete with self-hosting,\nso read this knowing where it comes from. The facts about Baileys below are the ones on our\n",[18,19,21],"a",{"href":20},"\u002Falternatives\u002Fbaileys\u002F","alternatives page",", each checked on the date shown there.",[24,25,27],"h2",{"id":26},"what-a-baileys-deployment-actually-costs","What a Baileys deployment actually costs",[11,29,30],{},"The library is free. What it does not include is everything around it, and in production that\nis most of the work.",[11,32,33,37,38,42,43,46,47,51],{},[34,35,36],"strong",{},"An always-on process per number."," Baileys holds a WebSocket to WhatsApp for as long as the\nnumber is \"online\". Your process has to stay up, reconnect on every ",[39,40,41],"code",{},"connection.update"," with a\n",[39,44,45],{},"DisconnectReason"," that permits it, and ",[48,49,50],"em",{},"not"," reconnect on the ones that do not — the difference\nbetween a hiccup and a logout, and between a logout and a ban, is a status code you have to\ninterpret correctly at 3 a.m.",[11,53,54,57,58,61,62,65],{},[34,55,56],{},"Session state you own."," ",[39,59,60],{},"useMultiFileAuthState"," writes the keys that make the linked device\n",[48,63,64],{},"your"," device. Lose them and the number needs re-pairing; leak them and someone else is your\ndevice. In practice that means a database-backed auth state, backups, and a story for what\nhappens when two replicas start with the same keys. The next major version of the library is\nexpected to change the auth-state format, which turns a version bump into a migration for every\nstored session.",[11,67,68,71,72,75],{},[34,69,70],{},"Media."," An inbound image is an encrypted blob and a key; ",[39,73,74],{},"downloadMediaMessage"," decrypts it,\nand then it is your problem to store, serve and expire. Outbound media needs the reverse.",[11,77,78,81,82,85],{},[34,79,80],{},"Pacing and ban risk."," Nothing in the library slows you down. A ",[39,83,84],{},"for"," loop over a contact list\nwill send as fast as the socket allows, which is exactly the pattern WhatsApp bans. Every team\nends up writing its own delay, its own warm-up, its own \"do not message people who never wrote\nto us\" rule — or does not, and loses the number.",[11,87,88,91],{},[34,89,90],{},"Keeping up."," As of September 2026 the current major version has sat on release candidates\nfor around ten months, so teams choose between an old stable line and a moving target. The issue\ntracker carries several hundred open issues, which makes it hard to tell whether a problem is\nyours, a known bug, or a protocol change nobody has caught up with. Version seven stopped\nemitting delivery acknowledgements for incoming messages because WhatsApp was banning numbers\nthat sent them too eagerly — a reminder that library behaviour and ban risk are the same\nsubject.",[11,93,94,97],{},[34,95,96],{},"Observability."," Which messages failed, why, and what the socket was doing at the time are\nthings you log, store and dashboard yourself.",[11,99,100],{},"None of this is a criticism of the library, which is remarkable engineering. It is a description\nof the job you take on when protocol access is your plumbing rather than your product.",[24,102,104],{"id":103},"what-a-hosted-api-takes-away","What a hosted API takes away",[11,106,107],{},"Be clear-eyed about the other direction too.",[109,110,111,127,133,139],"ul",{},[112,113,114,117,118,122,123,126],"li",{},[34,115,116],{},"Protocol access."," Baileys hands you every field WhatsApp sends. Wapito hands you a curated\ncontract: a stable message object, fourteen event types, a fixed set of endpoints. If you rely\non an obscure field, check the ",[18,119,121],{"href":120},"\u002Fdocs\u002Fapi\u002F","API reference"," before you commit; ",[39,124,125],{},"include_raw"," on\nthe channel puts the engine's raw payload beside every message, but the contract is the\ncontract.",[112,128,129,132],{},[34,130,131],{},"A dependency on someone else's uptime."," You trade your on-call for ours.",[112,134,135,138],{},[34,136,137],{},"A bill."," A Sandbox channel is free and limited; a Premium channel is a flat monthly price\nper number. Against an engineer-day a month of upkeep it is usually not close, but it is a line\nitem where there was none.",[112,140,141,144,145,148,149,153],{},[34,142,143],{},"Ban risk does not go away."," It changes who implements the controls. The warm-up ladder, the\ncold-send guard and the pacing are on by default and cannot be switched off, which is safer\nthan a hurried ",[39,146,147],{},"setTimeout",", but a number sending unwanted messages is still a number that\nwill be banned. The ",[18,150,152],{"href":151},"\u002Fdocs\u002Fanti-ban-guide\u002F","anti-ban guide"," is the honest version.",[24,155,157],{"id":156},"the-concept-map","The concept map",[11,159,160],{},"Most Baileys code falls into a dozen calls. Here is each one and its equivalent, with the\nreference page where the full request and response live.",[162,163,164,180],"table",{},[165,166,167],"thead",{},[168,169,170,174,177],"tr",{},[171,172,173],"th",{},"Baileys",[171,175,176],{},"Wapito",[171,178,179],{},"Notes",[181,182,183,209,236,276,299,332,361,383,399,421,439,454,472,495,510,524,538],"tbody",{},[168,184,185,195,202],{},[186,187,188,191,192],"td",{},[39,189,190],{},"makeWASocket()"," + ",[39,193,194],{},"useMultiFileAuthState()",[186,196,197,198,201],{},"A ",[34,199,200],{},"channel"," in the dashboard",[186,203,204,205,208],{},"No session files. The channel ",[48,206,207],{},"is"," the linked device; Wapito keeps its state.",[168,210,211,223,233],{},[186,212,213,216,217,220,221],{},[39,214,215],{},"sock.requestPairingCode(phone)"," \u002F the ",[39,218,219],{},"qr"," field of ",[39,222,41],{},[186,224,225,229,230],{},[18,226,228],{"href":227},"\u002Fwhatsapp-api\u002Fconnect-number-qr-pairing\u002F","Connect tab",", or ",[39,231,232],{},"POST \u002Fchannel\u002Fpairing-code",[186,234,235],{},"Pairing code first, QR one link away.",[168,237,238,243,249],{},[186,239,240],{},[39,241,242],{},"sock.ev.on('connection.update')",[186,244,245,246,248],{},"The ",[39,247,200],{}," webhook event",[186,250,251,254,255,258,259,262,263,262,266,262,269,272,273,275],{},[39,252,253],{},"status"," and ",[39,256,257],{},"reason"," (",[39,260,261],{},"banned",", ",[39,264,265],{},"logged_out",[39,267,268],{},"user_logout",[39,270,271],{},"engine_failed",") instead of ",[39,274,45],{}," arithmetic.",[168,277,278,283,289],{},[186,279,280],{},[39,281,282],{},"sock.ev.on('messages.upsert')",[186,284,285,286],{},"A webhook subscribed to ",[39,287,288],{},"messages",[186,290,291,294,295,298],{},[39,292,293],{},"POST \u002Fwebhooks"," with ",[39,296,297],{},"events: [\"messages\"]","; signed deliveries, retried on failure.",[168,300,301,307,312],{},[186,302,303,306],{},[39,304,305],{},"sock.ev.on('messages.update')"," (acks)",[186,308,309],{},[39,310,311],{},"messages.status",[186,313,314,262,317,262,320,262,323,262,326,262,329],{},[39,315,316],{},"failed",[39,318,319],{},"pending",[39,321,322],{},"sent",[39,324,325],{},"delivered",[39,327,328],{},"read",[39,330,331],{},"played",[168,333,334,339,344],{},[186,335,336],{},[39,337,338],{},"sock.sendMessage(jid, { text })",[186,340,341],{},[39,342,343],{},"POST \u002Fmessages\u002Ftext",[186,345,346,350,351,262,354,262,357,360],{},[18,347,349],{"href":348},"\u002Fwhatsapp-api\u002Fsend-message-without-template\u002F","Send without a template",". ",[39,352,353],{},"quoted",[39,355,356],{},"mentions",[39,358,359],{},"typing_time"," are body fields.",[168,362,363,368,373],{},[186,364,365],{},[39,366,367],{},"sock.sendMessage(jid, { image: { url }, caption })",[186,369,370],{},[39,371,372],{},"POST \u002Fmessages\u002Fimage",[186,374,375,378,379,382],{},[39,376,377],{},"media"," takes a URL, a data URI or an uploaded ",[39,380,381],{},"med_"," id.",[168,384,385,390,396],{},[186,386,387],{},[39,388,389],{},"downloadMediaMessage(msg)",[186,391,245,392,395],{},[39,393,394],{},"link"," on every media message",[186,397,398],{},"Already decrypted, stored, signed; 24 h on Sandbox, 7 days on Premium.",[168,400,401,406,414],{},[186,402,403],{},[39,404,405],{},"sock.sendPresenceUpdate('composing', jid)",[186,407,408,410,411],{},[39,409,359],{}," on the send, or ",[39,412,413],{},"POST \u002Fpresence\u002F{chat_id}",[186,415,416,417,420],{},"Automatic with ",[39,418,419],{},"typing_simulation: \"auto\"",".",[168,422,423,428,437],{},[186,424,425],{},[39,426,427],{},"sock.readMessages([key])",[186,429,430,229,433,436],{},[39,431,432],{},"PUT \u002Fmessages\u002F{id}\u002Fread",[39,434,435],{},"auto_read"," in settings",[186,438],{},[168,440,441,446,451],{},[186,442,443],{},[39,444,445],{},"sock.onWhatsApp(number)",[186,447,448],{},[39,449,450],{},"POST \u002Fcontacts\u002Fcheck",[186,452,453],{},"Up to fifty numbers per call, paced, quota-counted.",[168,455,456,461,466],{},[186,457,458],{},[39,459,460],{},"sock.groupCreate(subject, participants)",[186,462,463],{},[39,464,465],{},"POST \u002Fgroups",[186,467,468,420],{},[18,469,471],{"href":470},"\u002Fwhatsapp-api\u002Fcreate-group\u002F","Create a group",[168,473,474,479,484],{},[186,475,476],{},[39,477,478],{},"sock.groupParticipantsUpdate(jid, ids, 'add')",[186,480,481],{},[39,482,483],{},"POST \u002Fgroups\u002F{id}\u002Fparticipants",[186,485,486,490,491,494],{},[18,487,489],{"href":488},"\u002Fwhatsapp-api\u002Fgroup-participants\u002F","Add participants","; ",[39,492,493],{},"invite_required"," per person instead of a silent skip.",[168,496,497,502,507],{},[186,498,499],{},[39,500,501],{},"sock.groupInviteCode(jid)",[186,503,504],{},[39,505,506],{},"GET \u002Fgroups\u002F{id}\u002Finvite",[186,508,509],{},"Returns the code and the full link.",[168,511,512,517,522],{},[186,513,514],{},[39,515,516],{},"sock.groupMetadata(jid)",[186,518,519],{},[39,520,521],{},"GET \u002Fgroups\u002F{id}",[186,523],{},[168,525,526,531,536],{},[186,527,528],{},[39,529,530],{},"sock.profilePictureUrl(jid)",[186,532,533],{},[39,534,535],{},"GET \u002Fcontacts\u002F{id}\u002Fprofile",[186,537],{},[168,539,540,549,552],{},[186,541,542,262,545,548],{},[39,543,544],{},"jidNormalizedUser()",[39,546,547],{},"@lid"," handling",[186,550,551],{},"Done for you",[186,553,554,555,558,559,562],{},"Ids come back as ",[39,556,557],{},"@s.whatsapp.net","; LIDs resolved where possible, ",[39,560,561],{},"from_lid"," otherwise.",[11,564,565],{},"Three shape differences to plan for:",[109,567,568,589,603],{},[112,569,570,573,574,254,577,580,581,584,585,588],{},[34,571,572],{},"Message ids."," Baileys gives you ",[39,575,576],{},"key.id",[39,578,579],{},"key.remoteJid"," separately. Wapito's ",[39,582,583],{},"id"," is\none string of the form ",[39,586,587],{},"\u003CfromMe>_\u003Cchat>_\u003Cid>"," — treat it as opaque and store it whole.",[112,590,591,594,595,598,599,602],{},[34,592,593],{},"Timestamps."," Baileys reports ",[39,596,597],{},"messageTimestamp"," in seconds; Wapito's ",[39,600,601],{},"timestamp"," is\nmilliseconds.",[112,604,605,608,609,612,613,616,617,621,622,420],{},[34,606,607],{},"Events are HTTP, not callbacks."," Your ",[39,610,611],{},"messages.upsert"," handler becomes an HTTPS endpoint\nthat verifies a signature and answers ",[39,614,615],{},"200"," fast. The ",[18,618,620],{"href":619},"\u002Fdocs\u002Fwebhooks\u002F","webhooks guide"," has\nthe verifier in Node, Python and PHP; delivery is at-least-once and unordered, so key on the\nenvelope ",[39,623,583],{},[24,625,627],{"id":626},"side-by-side-send-and-receive","Side by side: send and receive",[11,629,630],{},"The most common Baileys program, and the same thing against Wapito.",[632,633,634,959],"code-group",{},[635,636,641],"pre",{"className":637,"code":638,"filename":173,"language":639,"meta":640,"style":640},"language-javascript shiki shiki-themes github-light-default github-dark-default","import makeWASocket, { useMultiFileAuthState, DisconnectReason } from '@whiskeysockets\u002Fbaileys';\n\nconst { state, saveCreds } = await useMultiFileAuthState('.\u002Fauth');\nconst sock = makeWASocket({ auth: state });\nsock.ev.on('creds.update', saveCreds);\n\nsock.ev.on('connection.update', ({ connection, lastDisconnect }) => {\n  if (connection === 'close') {\n    const code = lastDisconnect?.error?.output?.statusCode;\n    if (code !== DisconnectReason.loggedOut) start(); \u002F\u002F your reconnect logic\n  }\n});\n\nsock.ev.on('messages.upsert', async ({ messages }) => {\n  for (const msg of messages) {\n    if (msg.key.fromMe || !msg.message?.conversation) continue;\n    await sock.sendMessage(msg.key.remoteJid, { text: 'Thanks, we will reply shortly.' });\n  }\n});\n","javascript","",[39,642,643,666,673,713,730,747,752,785,803,817,842,848,854,859,887,906,928,949,954],{"__ignoreMap":640},[644,645,648,652,656,659,663],"span",{"class":646,"line":647},"line",1,[644,649,651],{"class":650},"sjeE4","import",[644,653,655],{"class":654},"s4rv2"," makeWASocket, { useMultiFileAuthState, DisconnectReason } ",[644,657,658],{"class":650},"from",[644,660,662],{"class":661},"sSVrQ"," '@whiskeysockets\u002Fbaileys'",[644,664,665],{"class":654},";\n",[644,667,669],{"class":646,"line":668},2,[644,670,672],{"emptyLinePlaceholder":671},true,"\n",[644,674,676,679,682,686,688,691,694,697,700,704,707,710],{"class":646,"line":675},3,[644,677,678],{"class":650},"const",[644,680,681],{"class":654}," { ",[644,683,685],{"class":684},"sHrmB","state",[644,687,262],{"class":654},[644,689,690],{"class":684},"saveCreds",[644,692,693],{"class":654}," } ",[644,695,696],{"class":650},"=",[644,698,699],{"class":650}," await",[644,701,703],{"class":702},"sbjLL"," useMultiFileAuthState",[644,705,706],{"class":654},"(",[644,708,709],{"class":661},"'.\u002Fauth'",[644,711,712],{"class":654},");\n",[644,714,716,718,721,724,727],{"class":646,"line":715},4,[644,717,678],{"class":650},[644,719,720],{"class":684}," sock",[644,722,723],{"class":650}," =",[644,725,726],{"class":702}," makeWASocket",[644,728,729],{"class":654},"({ auth: state });\n",[644,731,733,736,739,741,744],{"class":646,"line":732},5,[644,734,735],{"class":654},"sock.ev.",[644,737,738],{"class":702},"on",[644,740,706],{"class":654},[644,742,743],{"class":661},"'creds.update'",[644,745,746],{"class":654},", saveCreds);\n",[644,748,750],{"class":646,"line":749},6,[644,751,672],{"emptyLinePlaceholder":671},[644,753,755,757,759,761,764,767,771,773,776,779,782],{"class":646,"line":754},7,[644,756,735],{"class":654},[644,758,738],{"class":702},[644,760,706],{"class":654},[644,762,763],{"class":661},"'connection.update'",[644,765,766],{"class":654},", ({ ",[644,768,770],{"class":769},"sTDnQ","connection",[644,772,262],{"class":654},[644,774,775],{"class":769},"lastDisconnect",[644,777,778],{"class":654}," }) ",[644,780,781],{"class":650},"=>",[644,783,784],{"class":654}," {\n",[644,786,788,791,794,797,800],{"class":646,"line":787},8,[644,789,790],{"class":650},"  if",[644,792,793],{"class":654}," (connection ",[644,795,796],{"class":650},"===",[644,798,799],{"class":661}," 'close'",[644,801,802],{"class":654},") {\n",[644,804,806,809,812,814],{"class":646,"line":805},9,[644,807,808],{"class":650},"    const",[644,810,811],{"class":684}," code",[644,813,723],{"class":650},[644,815,816],{"class":654}," lastDisconnect?.error?.output?.statusCode;\n",[644,818,820,823,826,829,832,835,838],{"class":646,"line":819},10,[644,821,822],{"class":650},"    if",[644,824,825],{"class":654}," (code ",[644,827,828],{"class":650},"!==",[644,830,831],{"class":654}," DisconnectReason.loggedOut) ",[644,833,834],{"class":702},"start",[644,836,837],{"class":654},"(); ",[644,839,841],{"class":840},"sU953","\u002F\u002F your reconnect logic\n",[644,843,845],{"class":646,"line":844},11,[644,846,847],{"class":654},"  }\n",[644,849,851],{"class":646,"line":850},12,[644,852,853],{"class":654},"});\n",[644,855,857],{"class":646,"line":856},13,[644,858,672],{"emptyLinePlaceholder":671},[644,860,862,864,866,868,871,873,876,879,881,883,885],{"class":646,"line":861},14,[644,863,735],{"class":654},[644,865,738],{"class":702},[644,867,706],{"class":654},[644,869,870],{"class":661},"'messages.upsert'",[644,872,262],{"class":654},[644,874,875],{"class":650},"async",[644,877,878],{"class":654}," ({ ",[644,880,288],{"class":769},[644,882,778],{"class":654},[644,884,781],{"class":650},[644,886,784],{"class":654},[644,888,890,893,895,897,900,903],{"class":646,"line":889},15,[644,891,892],{"class":650},"  for",[644,894,258],{"class":654},[644,896,678],{"class":650},[644,898,899],{"class":684}," msg",[644,901,902],{"class":650}," of",[644,904,905],{"class":654}," messages) {\n",[644,907,909,911,914,917,920,923,926],{"class":646,"line":908},16,[644,910,822],{"class":650},[644,912,913],{"class":654}," (msg.key.fromMe ",[644,915,916],{"class":650},"||",[644,918,919],{"class":650}," !",[644,921,922],{"class":654},"msg.message?.conversation) ",[644,924,925],{"class":650},"continue",[644,927,665],{"class":654},[644,929,931,934,937,940,943,946],{"class":646,"line":930},17,[644,932,933],{"class":650},"    await",[644,935,936],{"class":654}," sock.",[644,938,939],{"class":702},"sendMessage",[644,941,942],{"class":654},"(msg.key.remoteJid, { text: ",[644,944,945],{"class":661},"'Thanks, we will reply shortly.'",[644,947,948],{"class":654}," });\n",[644,950,952],{"class":646,"line":951},18,[644,953,847],{"class":654},[644,955,957],{"class":646,"line":956},19,[644,958,853],{"class":654},[635,960,962],{"className":637,"code":961,"filename":176,"language":639,"meta":640,"style":640},"import crypto from 'node:crypto';\nimport express from 'express';\n\nconst app = express();\nconst SECRET = process.env.WAPITO_WEBHOOK_SECRET;\nconst TOKEN = process.env.WAPITO_TOKEN;\n\nfunction verify(raw, header) {\n  const parts = Object.fromEntries((header ?? '').split(',').map((p) => p.split('=')));\n  if (!parts.t || !parts.v1 || Math.abs(Date.now() - Number(parts.t)) > 300_000) return false;\n  const expected = crypto.createHmac('sha256', SECRET).update(`${parts.t}.`).update(raw).digest('hex');\n  return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(parts.v1, 'hex'));\n}\n\napp.post('\u002Fwapito', express.raw({ type: 'application\u002Fjson' }), async (req, res) => {\n  if (!verify(req.body, req.get('X-Wapito-Signature'))) return res.sendStatus(401);\n  res.sendStatus(200); \u002F\u002F acknowledge first; the reply below is not on the clock\n\n  const { event, data } = JSON.parse(req.body.toString('utf8'));\n  if (event !== 'messages' || data.from_me || data.type !== 'text') return;\n\n  await fetch('https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fmessages\u002Ftext', {\n    method: 'POST',\n    headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application\u002Fjson' },\n    body: JSON.stringify({ to: data.chat_id, body: 'Thanks, we will reply shortly.', quoted: data.id }),\n  });\n});\n\napp.listen(3000);\n",[39,963,964,978,992,996,1011,1028,1044,1048,1068,1132,1193,1256,1289,1294,1298,1343,1383,1400,1404,1443,1478,1483,1500,1512,1540,1562,1568,1573,1578],{"__ignoreMap":640},[644,965,966,968,971,973,976],{"class":646,"line":647},[644,967,651],{"class":650},[644,969,970],{"class":654}," crypto ",[644,972,658],{"class":650},[644,974,975],{"class":661}," 'node:crypto'",[644,977,665],{"class":654},[644,979,980,982,985,987,990],{"class":646,"line":668},[644,981,651],{"class":650},[644,983,984],{"class":654}," express ",[644,986,658],{"class":650},[644,988,989],{"class":661}," 'express'",[644,991,665],{"class":654},[644,993,994],{"class":646,"line":675},[644,995,672],{"emptyLinePlaceholder":671},[644,997,998,1000,1003,1005,1008],{"class":646,"line":715},[644,999,678],{"class":650},[644,1001,1002],{"class":684}," app",[644,1004,723],{"class":650},[644,1006,1007],{"class":702}," express",[644,1009,1010],{"class":654},"();\n",[644,1012,1013,1015,1018,1020,1023,1026],{"class":646,"line":732},[644,1014,678],{"class":650},[644,1016,1017],{"class":684}," SECRET",[644,1019,723],{"class":650},[644,1021,1022],{"class":654}," process.env.",[644,1024,1025],{"class":684},"WAPITO_WEBHOOK_SECRET",[644,1027,665],{"class":654},[644,1029,1030,1032,1035,1037,1039,1042],{"class":646,"line":749},[644,1031,678],{"class":650},[644,1033,1034],{"class":684}," TOKEN",[644,1036,723],{"class":650},[644,1038,1022],{"class":654},[644,1040,1041],{"class":684},"WAPITO_TOKEN",[644,1043,665],{"class":654},[644,1045,1046],{"class":646,"line":754},[644,1047,672],{"emptyLinePlaceholder":671},[644,1049,1050,1053,1056,1058,1061,1063,1066],{"class":646,"line":787},[644,1051,1052],{"class":650},"function",[644,1054,1055],{"class":702}," verify",[644,1057,706],{"class":654},[644,1059,1060],{"class":769},"raw",[644,1062,262],{"class":654},[644,1064,1065],{"class":769},"header",[644,1067,802],{"class":654},[644,1069,1070,1073,1076,1078,1081,1084,1087,1090,1093,1096,1099,1101,1104,1106,1109,1112,1114,1117,1119,1122,1124,1126,1129],{"class":646,"line":805},[644,1071,1072],{"class":650},"  const",[644,1074,1075],{"class":684}," parts",[644,1077,723],{"class":650},[644,1079,1080],{"class":654}," Object.",[644,1082,1083],{"class":702},"fromEntries",[644,1085,1086],{"class":654},"((header ",[644,1088,1089],{"class":650},"??",[644,1091,1092],{"class":661}," ''",[644,1094,1095],{"class":654},").",[644,1097,1098],{"class":702},"split",[644,1100,706],{"class":654},[644,1102,1103],{"class":661},"','",[644,1105,1095],{"class":654},[644,1107,1108],{"class":702},"map",[644,1110,1111],{"class":654},"((",[644,1113,11],{"class":769},[644,1115,1116],{"class":654},") ",[644,1118,781],{"class":650},[644,1120,1121],{"class":654}," p.",[644,1123,1098],{"class":702},[644,1125,706],{"class":654},[644,1127,1128],{"class":661},"'='",[644,1130,1131],{"class":654},")));\n",[644,1133,1134,1136,1138,1141,1144,1146,1148,1151,1153,1156,1159,1162,1165,1168,1171,1174,1177,1180,1183,1185,1188,1191],{"class":646,"line":819},[644,1135,790],{"class":650},[644,1137,258],{"class":654},[644,1139,1140],{"class":650},"!",[644,1142,1143],{"class":654},"parts.t ",[644,1145,916],{"class":650},[644,1147,919],{"class":650},[644,1149,1150],{"class":654},"parts.v1 ",[644,1152,916],{"class":650},[644,1154,1155],{"class":654}," Math.",[644,1157,1158],{"class":702},"abs",[644,1160,1161],{"class":654},"(Date.",[644,1163,1164],{"class":702},"now",[644,1166,1167],{"class":654},"() ",[644,1169,1170],{"class":650},"-",[644,1172,1173],{"class":702}," Number",[644,1175,1176],{"class":654},"(parts.t)) ",[644,1178,1179],{"class":650},">",[644,1181,1182],{"class":684}," 300_000",[644,1184,1116],{"class":654},[644,1186,1187],{"class":650},"return",[644,1189,1190],{"class":684}," false",[644,1192,665],{"class":654},[644,1194,1195,1197,1200,1202,1205,1208,1210,1213,1215,1218,1220,1223,1225,1228,1231,1233,1236,1239,1241,1243,1246,1249,1251,1254],{"class":646,"line":844},[644,1196,1072],{"class":650},[644,1198,1199],{"class":684}," expected",[644,1201,723],{"class":650},[644,1203,1204],{"class":654}," crypto.",[644,1206,1207],{"class":702},"createHmac",[644,1209,706],{"class":654},[644,1211,1212],{"class":661},"'sha256'",[644,1214,262],{"class":654},[644,1216,1217],{"class":684},"SECRET",[644,1219,1095],{"class":654},[644,1221,1222],{"class":702},"update",[644,1224,706],{"class":654},[644,1226,1227],{"class":661},"`${",[644,1229,1230],{"class":654},"parts",[644,1232,420],{"class":661},[644,1234,1235],{"class":654},"t",[644,1237,1238],{"class":661},"}.`",[644,1240,1095],{"class":654},[644,1242,1222],{"class":702},[644,1244,1245],{"class":654},"(raw).",[644,1247,1248],{"class":702},"digest",[644,1250,706],{"class":654},[644,1252,1253],{"class":661},"'hex'",[644,1255,712],{"class":654},[644,1257,1258,1261,1263,1266,1269,1271,1274,1276,1279,1281,1284,1286],{"class":646,"line":850},[644,1259,1260],{"class":650},"  return",[644,1262,1204],{"class":654},[644,1264,1265],{"class":702},"timingSafeEqual",[644,1267,1268],{"class":654},"(Buffer.",[644,1270,658],{"class":702},[644,1272,1273],{"class":654},"(expected, ",[644,1275,1253],{"class":661},[644,1277,1278],{"class":654},"), Buffer.",[644,1280,658],{"class":702},[644,1282,1283],{"class":654},"(parts.v1, ",[644,1285,1253],{"class":661},[644,1287,1288],{"class":654},"));\n",[644,1290,1291],{"class":646,"line":856},[644,1292,1293],{"class":654},"}\n",[644,1295,1296],{"class":646,"line":861},[644,1297,672],{"emptyLinePlaceholder":671},[644,1299,1300,1303,1306,1308,1311,1314,1316,1319,1322,1325,1327,1329,1332,1334,1337,1339,1341],{"class":646,"line":889},[644,1301,1302],{"class":654},"app.",[644,1304,1305],{"class":702},"post",[644,1307,706],{"class":654},[644,1309,1310],{"class":661},"'\u002Fwapito'",[644,1312,1313],{"class":654},", express.",[644,1315,1060],{"class":702},[644,1317,1318],{"class":654},"({ type: ",[644,1320,1321],{"class":661},"'application\u002Fjson'",[644,1323,1324],{"class":654}," }), ",[644,1326,875],{"class":650},[644,1328,258],{"class":654},[644,1330,1331],{"class":769},"req",[644,1333,262],{"class":654},[644,1335,1336],{"class":769},"res",[644,1338,1116],{"class":654},[644,1340,781],{"class":650},[644,1342,784],{"class":654},[644,1344,1345,1347,1349,1351,1354,1357,1360,1362,1365,1368,1370,1373,1376,1378,1381],{"class":646,"line":908},[644,1346,790],{"class":650},[644,1348,258],{"class":654},[644,1350,1140],{"class":650},[644,1352,1353],{"class":702},"verify",[644,1355,1356],{"class":654},"(req.body, req.",[644,1358,1359],{"class":702},"get",[644,1361,706],{"class":654},[644,1363,1364],{"class":661},"'X-Wapito-Signature'",[644,1366,1367],{"class":654},"))) ",[644,1369,1187],{"class":650},[644,1371,1372],{"class":654}," res.",[644,1374,1375],{"class":702},"sendStatus",[644,1377,706],{"class":654},[644,1379,1380],{"class":684},"401",[644,1382,712],{"class":654},[644,1384,1385,1388,1390,1392,1394,1397],{"class":646,"line":930},[644,1386,1387],{"class":654},"  res.",[644,1389,1375],{"class":702},[644,1391,706],{"class":654},[644,1393,615],{"class":684},[644,1395,1396],{"class":654},"); ",[644,1398,1399],{"class":840},"\u002F\u002F acknowledge first; the reply below is not on the clock\n",[644,1401,1402],{"class":646,"line":951},[644,1403,672],{"emptyLinePlaceholder":671},[644,1405,1406,1408,1410,1413,1415,1418,1420,1422,1425,1427,1430,1433,1436,1438,1441],{"class":646,"line":956},[644,1407,1072],{"class":650},[644,1409,681],{"class":654},[644,1411,1412],{"class":684},"event",[644,1414,262],{"class":654},[644,1416,1417],{"class":684},"data",[644,1419,693],{"class":654},[644,1421,696],{"class":650},[644,1423,1424],{"class":684}," JSON",[644,1426,420],{"class":654},[644,1428,1429],{"class":702},"parse",[644,1431,1432],{"class":654},"(req.body.",[644,1434,1435],{"class":702},"toString",[644,1437,706],{"class":654},[644,1439,1440],{"class":661},"'utf8'",[644,1442,1288],{"class":654},[644,1444,1446,1448,1451,1453,1456,1459,1462,1464,1467,1469,1472,1474,1476],{"class":646,"line":1445},20,[644,1447,790],{"class":650},[644,1449,1450],{"class":654}," (event ",[644,1452,828],{"class":650},[644,1454,1455],{"class":661}," 'messages'",[644,1457,1458],{"class":650}," ||",[644,1460,1461],{"class":654}," data.from_me ",[644,1463,916],{"class":650},[644,1465,1466],{"class":654}," data.type ",[644,1468,828],{"class":650},[644,1470,1471],{"class":661}," 'text'",[644,1473,1116],{"class":654},[644,1475,1187],{"class":650},[644,1477,665],{"class":654},[644,1479,1481],{"class":646,"line":1480},21,[644,1482,672],{"emptyLinePlaceholder":671},[644,1484,1486,1489,1492,1494,1497],{"class":646,"line":1485},22,[644,1487,1488],{"class":650},"  await",[644,1490,1491],{"class":702}," fetch",[644,1493,706],{"class":654},[644,1495,1496],{"class":661},"'https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fmessages\u002Ftext'",[644,1498,1499],{"class":654},", {\n",[644,1501,1503,1506,1509],{"class":646,"line":1502},23,[644,1504,1505],{"class":654},"    method: ",[644,1507,1508],{"class":661},"'POST'",[644,1510,1511],{"class":654},",\n",[644,1513,1515,1518,1521,1524,1527,1529,1532,1535,1537],{"class":646,"line":1514},24,[644,1516,1517],{"class":654},"    headers: { Authorization: ",[644,1519,1520],{"class":661},"`Bearer ${",[644,1522,1523],{"class":684},"TOKEN",[644,1525,1526],{"class":661},"}`",[644,1528,262],{"class":654},[644,1530,1531],{"class":661},"'Content-Type'",[644,1533,1534],{"class":654},": ",[644,1536,1321],{"class":661},[644,1538,1539],{"class":654}," },\n",[644,1541,1543,1546,1549,1551,1554,1557,1559],{"class":646,"line":1542},25,[644,1544,1545],{"class":654},"    body: ",[644,1547,1548],{"class":684},"JSON",[644,1550,420],{"class":654},[644,1552,1553],{"class":702},"stringify",[644,1555,1556],{"class":654},"({ to: data.chat_id, body: ",[644,1558,945],{"class":661},[644,1560,1561],{"class":654},", quoted: data.id }),\n",[644,1563,1565],{"class":646,"line":1564},26,[644,1566,1567],{"class":654},"  });\n",[644,1569,1571],{"class":646,"line":1570},27,[644,1572,853],{"class":654},[644,1574,1576],{"class":646,"line":1575},28,[644,1577,672],{"emptyLinePlaceholder":671},[644,1579,1581,1583,1586,1588,1591],{"class":646,"line":1580},29,[644,1582,1302],{"class":654},[644,1584,1585],{"class":702},"listen",[644,1587,706],{"class":654},[644,1589,1590],{"class":684},"3000",[644,1592,712],{"class":654},[11,1594,1595,1596,1598],{},"The Wapito side has no socket, no auth folder and no reconnect branch, and the reply is paced,\ntyped and rate-limited by the platform rather than by whatever ",[39,1597,147],{}," the Baileys version\nforgot. What it has instead is a signature to verify and an HTTP status to return quickly.",[24,1600,1602],{"id":1601},"what-has-to-be-re-paired-and-what-survives","What has to be re-paired, and what survives",[11,1604,1605,1606,1609],{},"A WhatsApp session is bound to the library and the device registration that created it. The\n",[39,1607,1608],{},"auth"," folder Baileys wrote cannot be imported into Wapito or into anything else; the number has\nto be linked again, as a new device. Plan it as a short maintenance window per number:",[109,1611,1612,1618,1628,1634],{},[112,1613,1614,1617],{},[34,1615,1616],{},"Survives:"," the number, the account, its contacts, every chat and group on the phone, the\ngroup memberships and admin roles, the profile. None of that lives in the linked device.",[112,1619,1620,1623,1624,1627],{},[34,1621,1622],{},"Replaced:"," the linked device. Log the Baileys device out first — from the phone's ",[48,1625,1626],{},"Linked\ndevices"," screen, or by letting Baileys receive the logout — before linking Wapito. A number can\nhave several linked devices, and leaving a dead one registered is both a security smell and\none more thing WhatsApp sees.",[112,1629,1630,1633],{},[34,1631,1632],{},"Not migrated:"," message history. Wapito stores what happens from the moment the channel is\nlinked; Baileys' history sync is not replayed. Keep your database, and stop assuming the\nprovider is your archive — it never was.",[112,1635,1636,1639],{},[34,1637,1638],{},"Reset:"," the warm-up ladder. A newly linked number starts at 50 messages on its first day and\nclimbs over a week. For a number that was already sending hundreds a day this is the part that\nneeds scheduling: link it a week before it takes production traffic, or migrate it during a\nquiet period.",[24,1641,1643],{"id":1642},"moving-one-number-at-a-time","Moving one number at a time",[11,1645,1646],{},"A Baileys deployment with several numbers should not be cut over in one evening. The shape that\nworks:",[11,1648,1649,1652,1653,1656],{},[34,1650,1651],{},"1. Inventory."," For each number: the process that runs it, the handlers it registers, the\ncalls it makes (grep for ",[39,1654,1655],{},"sock.","), the daily volume, and whether it ever messages people who\ndid not write first. That last column decides how much the cold-send guard will change its\nbehaviour.",[11,1658,1659,1662,1663,1665,1666,1669,1670,1673],{},[34,1660,1661],{},"2. Build the receiver once."," One webhook endpoint that verifies signatures, writes the\nenvelope to a queue and returns ",[39,1664,615],{},". Route on ",[39,1667,1668],{},"X-Wapito-Channel"," so a single endpoint serves\nevery number. Test it with ",[39,1671,1672],{},"POST \u002Fwebhooks\u002F{id}\u002Ftest"," against a Sandbox channel before any\nproduction number moves.",[11,1675,1676,1679],{},[34,1677,1678],{},"3. Port the handlers behind a flag."," Wrap each Baileys handler's body in a function that\ntakes your own normalised message shape, and write a second adapter from the Wapito event to\nthat shape. The business logic does not know which side is feeding it.",[11,1681,1682,1685,1686,1688,1689,1692,1693,1695,1696,1700],{},[34,1683,1684],{},"4. Move the quietest number first."," Log the Baileys device out, link the number to a\n",[18,1687,200],{"href":227},", flip the flag, watch the logs for a day.\nExpect ",[39,1690,1691],{},"429","s from the warm-up ladder and the cold-send guard if the number was busy; they are\nthe platform doing what your ",[39,1694,147],{}," used to, and the ",[18,1697,1699],{"href":1698},"\u002Fdocs\u002Frate-limits\u002F","rate limits page","\nexplains each code.",[11,1702,1703,1706],{},[34,1704,1705],{},"5. Repeat, one number a day."," Each number gets its own token; nothing in the code changes\nbetween them except an environment variable.",[11,1708,1709,1712],{},[34,1710,1711],{},"6. Turn the Baileys process off last",", once no number points at it, and delete the auth\nfolders — they are credentials, and they are now credentials to devices that no longer exist.",[11,1714,1715],{},"Never run the same number through both at once. Two linked devices driven by two automations is\nthe exact pattern an account-takeover check flags, and it is the one migration mistake that\ncosts a number.",[24,1717,1719],{"id":1718},"is-it-the-right-move","Is it the right move?",[11,1721,1722],{},"Keep Baileys if protocol access is your product: you are building a client, you need a field\nthe contract does not carry, or your volume is one number and you enjoy the upkeep. Move if the\nsocket, the session files, the reconnect logic and the ban-risk controls are things you would\nrather not own, and you are prepared to pay a flat price per number to stop owning them. Most\nteams that ask the question are already past the point where the answer is in doubt; what they\nneed is the map above and a quiet week to walk it.",[11,1724,245,1725,1728,1729,1733],{},[18,1726,1727],{"href":20},"Baileys alternatives page"," has the pricing comparison with sources\nand dates, and the ",[18,1730,1732],{"href":1731},"\u002Falternatives\u002Fwhatsmeow\u002F","whatsmeow page"," explains why the default engine\nunderneath a Wapito channel is the other library.",[1735,1736,1737],"style",{},"html pre.shiki code .sjeE4, html code.shiki .sjeE4{--shiki-default:#CF222E;--shiki-dark:#FF7B72}html pre.shiki code .s4rv2, html code.shiki .s4rv2{--shiki-default:#1F2328;--shiki-dark:#E6EDF3}html pre.shiki code .sSVrQ, html code.shiki .sSVrQ{--shiki-default:#0A3069;--shiki-dark:#A5D6FF}html pre.shiki code .sHrmB, html code.shiki .sHrmB{--shiki-default:#0550AE;--shiki-dark:#79C0FF}html pre.shiki code .sbjLL, html code.shiki .sbjLL{--shiki-default:#8250DF;--shiki-dark:#D2A8FF}html pre.shiki code .sTDnQ, html code.shiki .sTDnQ{--shiki-default:#953800;--shiki-dark:#FFA657}html pre.shiki code .sU953, html code.shiki .sU953{--shiki-default:#6E7781;--shiki-dark:#8B949E}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":640,"searchDepth":668,"depth":668,"links":1739},[1740,1741,1742,1743,1744,1745,1746],{"id":26,"depth":668,"text":27},{"id":103,"depth":668,"text":104},{"id":156,"depth":668,"text":157},{"id":626,"depth":668,"text":627},{"id":1601,"depth":668,"text":1602},{"id":1642,"depth":668,"text":1643},{"id":1718,"depth":668,"text":1719},"2026-09-15","What a Baileys deployment really costs to run, which concepts map one to one, what has to be re-paired, and how to move one number at a time.","md",[1751,1752,1753,1754],"connect-number-qr-pairing","send-message-without-template","create-group","group-participants",{},"\u002Fblog\u002Fmigrating-from-baileys-to-a-hosted-api",{"title":5,"description":1748},"blog\u002Fmigrating-from-baileys-to-a-hosted-api",[1760,1761,1762],"migration","alternatives","operations",null,"041pw7YnGSi9s60z8TFPUSav6OtNfp7GoliXQZewqk8",1790464894221]