Groups & contacts
Reading the address book, writing to groups, and what a LID is.
Group writes are the highest ban risk
Higher than send volume, in the research this project is built on. Creating a group, adding or promoting participants, leaving, blocking — each one touches real people on a real account. Rehearse every one of them on a sandbox first, where the participants are invented and the read-back still works.
Reading contacts
# The whole address book, learned from traffic.
curl https://api.wapi.crafter.run/api/contacts -H "Authorization: Bearer $KEY"
# One contact.
curl https://api.wapi.crafter.run/api/contacts/+51999888777 -H "Authorization: Bearer $KEY"
# Is a number on WhatsApp at all?
curl https://api.wapi.crafter.run/api/on-whatsapp/+51999888777 -H "Authorization: Bearer $KEY"imgUrl and status are always null in a list. A picture and an "about" string are
per-contact fetches, so a list call never makes N of them. Ask for the contact individually, or
call the picture endpoint, if you need either.
One asymmetry to know about: a list is keyed on jid, while GET /api/contacts/{number} returns
id. That is WasenderAPI's inconsistency, reproduced rather than tidied.
Pagination
Both /api/contacts and /api/groups take ?paginated=true, which changes the response
shape — data becomes { items, pagination } instead of an array:
curl "https://api.wapi.crafter.run/api/contacts?paginated=true&page=1&limit=20" \
-H "Authorization: Bearer $KEY"{ "success": true,
"data": { "items": [ ... ],
"pagination": { "page": 1, "limit": 20, "total": 38, "totalPages": 2 } } }limit defaults to 20 and caps at 500. totalPages is ceil(total / limit), and page echoes
what you asked for.
Writing to contacts
Contacts can be blocked, unblocked, and given a saved name:
curl -X PUT https://api.wapi.crafter.run/api/contacts \
-H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' \
-d '{"jid":"51999888777@s.whatsapp.net","name":"Ada"}'That name is wapi's, not WhatsApp's. It does not appear on the linked phone.
Groups
Groups can be created, left, renamed and re-described; participants added, removed, promoted and demoted; invite links read, inspected before joining, and accepted.
curl https://api.wapi.crafter.run/api/groups -H "Authorization: Bearer $KEY"
curl https://api.wapi.crafter.run/api/groups/<jid>/metadata -H "Authorization: Bearer $KEY"
curl https://api.wapi.crafter.run/api/groups/<jid>/participants -H "Authorization: Bearer $KEY"To send to a group, use its JID as to on the ordinary send endpoint — see
Sending messages.
Three shapes that disagree
Reproduced deliberately, because their SDK depends on them:
| Endpoint | Returns |
|---|---|
participants/add, participants/remove | Per-participant array of {status, jid, message} |
participants/update (promote/demote) | {participants: [jid]} — no status at all |
invite-link | inviteLink beside success, not under data — the sixth success envelope |
On participants/update, compare what you sent against what came back: that is the only way to
spot a partial failure, because nothing reports one.
What goes wrong
| Status | Meaning |
|---|---|
403 | A group write with a Personal Access Token; these need the session key |
404 | The JID is not a group this session belongs to |
422 | A phone number in a form WhatsApp rejected, or an empty participant list |
503 | Session not connected |
Two silent failures matter more than any of those. participants/update returns no status per
participant, so a promote that WhatsApp refused looks identical to one it accepted — compare what
you sent against what came back. And adding somebody whose privacy settings forbid it succeeds at
the API level and does nothing on WhatsApp; the per-participant status on participants/add is
where that shows up.
LIDs
Group participants and inbound senders often appear as …@lid rather than a phone number. That is
WhatsApp's newer identity format — not an error, and not something wapi introduced.
curl https://api.wapi.crafter.run/api/lid-from-pn/+51999888777 -H "Authorization: Bearer $KEY"
curl https://api.wapi.crafter.run/api/pn-from-lid/46274715893950@lid -H "Authorization: Bearer $KEY"pn-from-lid resolves where a mapping has been observed. A miss is legitimate: resolution only
works reliably in one direction, so write code that copes with null rather than treating it as
a failure.
Operations covered
GET/api/contactssession keyGET/api/contacts/{contactPhoneNumber}session keyGET/api/contacts/{contactPhoneNumber}/picturesession keyPUT/api/contactssession keyPOST/api/contacts/{contactPhoneNumber}/blocksession keyPOST/api/contacts/{contactPhoneNumber}/unblocksession keyGET/api/on-whatsapp/{contact_identifier}session keyGET/api/lid-from-pn/{pn}session keyGET/api/pn-from-lid/{lid}session keyGET/api/groupssession keyPOST/api/groupssession keyGET/api/groups/{groupJid}/metadatasession keyGET/api/groups/{groupJid}/participantssession keyGET/api/groups/{groupJid}/picturesession keyGET/api/groups/{groupJid}/invite-linksession keyGET/api/groups/invite/{inviteCode}session keyPOST/api/groups/invite/acceptsession keyPOST/api/groups/{groupJid}/participants/addsession keyPOST/api/groups/{groupJid}/participants/removesession keyPUT/api/groups/{groupId}/participants/updatesession keyPUT/api/groups/{groupJid}/settingssession keyPOST/api/groups/{groupId}/leavesession key