[{"data":1,"prerenderedAt":144},["ShallowReactive",2],{"pseo:platforms:google-sheets:create-group":3},{"ban_risk":4,"faqs":5,"feature":18,"goal":22,"platform":23,"prerequisites":30,"related":34,"seo":61,"steps":77,"test":118,"tier":119,"troubleshooting":120,"updated":75,"url":71,"verified":143},"high",[6,9,12,15],{"a":7,"q":8},"Safer than in the code, which is the alternative on this platform. Script Properties are visible only to people who can edit the script project, not to everyone who can view or edit the sheet, and they are not copied when someone duplicates the spreadsheet. If a collaborator with editor access leaves, rotate the token from the Wapito dashboard and update the property.","Is it safe to keep the token in Script Properties?",{"a":10,"q":11},"The cell was formatted as a number, and Sheets converted the long digit string into scientific notation, losing the trailing digits. The id in that cell no longer names any group. Format the Group id column as plain text, clear the cell, and run the function again for that row; the script writes the id as a string, but a cell format wins over the value's type.","Why did the group id turn into a number with E+ in it?",{"a":13,"q":14},"Each row is one UrlFetchApp call, and Apps Script allows tens of thousands of those per day on an ordinary account, so the quota is not the constraint. The constraint is WhatsApp: creating groups in bulk from one number is the clearest automation signal there is. The batch cap in the last step is there for the number, and it makes a big sheet take days rather than minutes on purpose.","Will a large sheet hit Apps Script quotas?",{"a":16,"q":17},"Yes. After writing the id back, post to the text endpoint with that id as the recipient, in the same run or in a second function that processes rows whose id is set and whose Sent cell is empty. Keep it to one message per group and give it a typing time, so the welcome arrives a few seconds after the group appears instead of the same instant.","Can the sheet also send a first message to each new group?",{"name":19,"slug":20,"url":21},"Create Group","create-group","\u002Fwhatsapp-api\u002Fcreate-group\u002F","Create a WhatsApp group from each row of a Google Sheet, with the group name and the members read from the row and the group id written back into it. An Apps Script function posts each row to Wapito: no add-on, no third party holding your data, and a re-run skips every row that already has an id.",{"http_module":24,"name":27,"slug":28,"url":29},{"docs_url":25,"name":26},"https:\u002F\u002Fdevelopers.google.com\u002Fapps-script\u002Freference\u002Furl-fetch\u002Furl-fetch-app","Apps Script UrlFetchApp.fetch()","Google Sheets","google-sheets","\u002Fintegrations\u002Fgoogle-sheets\u002F",[31,32,33],"A Google Sheet with one row per group: a name column, a description column and one column of phone numbers separated by commas.","Edit access to the sheet so you can open Extensions and then Apps Script.","A connected Wapito channel and its channel token, ready to paste into Script Properties rather than into the code.",{"errors":35,"operations":51,"pillar":21,"platform":29},[36,39,42,45,48],{"title":37,"url":38},"invalid_request (400)","\u002Fdocs\u002Ferrors\u002Finvalid-request\u002F",{"title":40,"url":41},"engine_error (502)","\u002Fdocs\u002Ferrors\u002Fengine-error\u002F",{"title":43,"url":44},"unauthorized (401)","\u002Fdocs\u002Ferrors\u002Funauthorized\u002F",{"title":46,"url":47},"channel_locked (403)","\u002Fdocs\u002Ferrors\u002Fchannel-locked\u002F",{"title":49,"url":50},"not_found (404)","\u002Fdocs\u002Ferrors\u002Fnot-found\u002F",[52,55,58],{"title":53,"url":54},"POST \u002Fgroups","\u002Fdocs\u002Fapi\u002Fgroups\u002Fcreate-group\u002F",{"title":56,"url":57},"GET \u002Fgroups\u002F{id}","\u002Fdocs\u002Fapi\u002Fgroups\u002Fget-group\u002F",{"title":59,"url":60},"GET \u002Fgroups\u002F{id}\u002Finvite","\u002Fdocs\u002Fapi\u002Fgroups\u002Fget-group-invite\u002F",{"breadcrumb":62,"canonical":72,"description":73,"h1":74,"lastmod":75,"title":76},[63,66,69,70],{"name":64,"url":65},"Home","\u002F",{"name":67,"url":68},"Integrations","\u002Fintegrations\u002F",{"name":27,"url":29},{"name":19,"url":71},"\u002Fintegrations\u002Fgoogle-sheets\u002Fcreate-group\u002F","https:\u002F\u002Fwapito.com\u002Fintegrations\u002Fgoogle-sheets\u002Fcreate-group\u002F","Create a WhatsApp group from each row of a Google Sheet, with the group name and the members read from the row and the group id written back into it.","Create a WhatsApp group from a Google Sheets row","2026-09-16","Create WhatsApp Groups from Google Sheets (No Code) | Wapito",[78,86,91,98,107,113],{"body":79,"field_map":80,"title":85},"Give the sheet four columns in this order: Group name, Description, Members and Group id. Members holds phone numbers in international format separated by commas, and Group id stays empty until the script fills it. Format the Group id column as plain text before the first run, because the id is a numeric string longer than a number cell can hold and Sheets would otherwise round it.",{"description":81,"group_id":82,"participants":83,"subject":84},"Column B — Description","Column D — written back by the script, formatted as plain text","Column C — Members, split on commas","Column A — Group name","Lay the sheet out with a write-back column",{"body":87,"field_map":88,"title":90},"Open Extensions, then Apps Script, then Project Settings, and add a script property named WAPITO_TOKEN whose value is your channel token. The code reads it with PropertiesService.getScriptProperties().getProperty('WAPITO_TOKEN'), so anyone you share the sheet with can see the groups but never the token, and rotating it is a change to one property rather than to the script.",{"Authorization":89},"Bearer + PropertiesService.getScriptProperties().getProperty('WAPITO_TOKEN')","Put the channel token in Script Properties",{"body":92,"field_map":93,"title":97},"In the script editor write a function that reads getDataRange().getValues(), skips the header row and every row whose Group id cell is already filled, and for each remaining row builds the payload: the name as the subject, the description, and the Members cell split on commas with each entry trimmed. Keep the function small; the sheet is the queue and the id column is the record of what has been done.",{"description":94,"participants":95,"subject":96},"row[1] — sent only when the cell is not empty","row[2].split(',').map(s => s.trim()).filter(Boolean)","row[0] — trimmed","Write createGroupsFromSheet()",{"body":99,"field_map":100,"title":106},"Post the payload to https:\u002F\u002Fapi.wapito.com\u002Fv1\u002Fgroups with method post, contentType application\u002Fjson, the Authorization header from Script Properties, payload set to JSON.stringify of the object, and muteHttpExceptions set to true. Without that last option a refused request throws before you can read the error body, and the error body is where Wapito tells you which number was not dialable.",{"contentType":101,"headers.Authorization":102,"method":103,"muteHttpExceptions":104,"payload":105},"application\u002Fjson","'Bearer ' + token","post","true","JSON.stringify({ subject, description, participants })","Call UrlFetchApp.fetch with muteHttpExceptions",{"body":108,"field_map":109,"title":112},"Read response.getResponseCode(); on 201 parse the body and write the id into the Group id cell of that row with setValue, prefixed with nothing and as a string. On any other code write the error code from the body into the same cell, prefixed with ERROR, so the row is visibly failed and the next run does not try it again until someone clears the cell.",{"error.code":110,"group_id":111},"JSON.parse(response.getContentText()).error.code → column D, prefixed with ERROR","JSON.parse(response.getContentText()).id → column D of the same row","Write the group id back into the row",{"body":114,"field_map":115,"title":117},"In Triggers add a time-driven trigger that runs the function every hour, and cap each run at a handful of rows with a counter, breaking out of the loop once it is reached. That keeps each execution well inside the Apps Script time limit, and it keeps group creation at a pace a person could plausibly manage, which matters more for the number's health than for the quota.",{"batch_size":116},"A constant in the script, for example 5 groups per run","Add a time-driven trigger with a small batch","Add one row whose Members cell holds only your own second phone, run createGroupsFromSheet from the editor, and authorise the script when prompted. Column D of that row should fill with an id ending in @g.us, the phone should show the new group, and running the function again should leave the row untouched.","t3b",[121,126,129,134,138],{"code":122,"http_status":123,"title":124,"url":125},"invalid_recipient",400,"Recipient is not valid","\u002Fdocs\u002Ferrors\u002Finvalid-recipient\u002F",{"code":127,"http_status":123,"title":128,"url":38},"invalid_request","Request failed validation",{"code":130,"http_status":131,"title":132,"url":133},"channel_not_connected",409,"Channel not connected","\u002Fdocs\u002Ferrors\u002Fchannel-not-connected\u002F",{"code":135,"http_status":136,"title":137,"url":44},"unauthorized",401,"Missing or invalid token",{"code":139,"http_status":140,"title":141,"url":142},"rate_limited",429,"Too many requests","\u002Fdocs\u002Ferrors\u002Frate-limited\u002F",false,1790464904327]