Guide8 min read

WhatsApp usernames API: message and look up @usernames

You can message a WhatsApp @username through an API and look one up without messaging anyone. With Mossmoon you pass to_username to the send endpoint or call the lookup endpoint, and replies arrive on your webhook keyed by a WhatsApp id instead of a phone number.

Published By the Mossmoon team


On this page
  1. 01What is a username?
  2. 02Message an @username
  3. 03Look up a username
  4. 04Username PINs
  5. 05Usernames on webhooks
  6. 06Storing contacts in a CRM
  7. 07Usernames in groups
  8. 08Limits
  9. 09Mossmoon vs goghl.ai
  10. 10FAQ

Yes, you can message a WhatsApp username through an API. With Mossmoon you pass to_username (plus username_pin if the person set one) to the send endpoint, or call POST /usernames/lookup to find the account without sending anything.

Messages from people with a username reach your webhook with from_username, and people reachable only by username have no phone number at all, so your CRM should key them on from_wa_id. Below are the real requests and payloads, the PIN rules and the limits. It is part of the contacts features on the $15 per line plan.

What is a WhatsApp username?

A WhatsApp username is a handle like @sarah.k that people can share instead of their phone number. WhatsApp is still rolling them out, so not everyone has one yet. Three facts shape how an API has to handle them:

No phone number
Someone reached only by username has a WhatsApp id ending in @lid, like 777000111222333@lid. That is an internal id, not a number: never dial or text its digits.
An optional PIN
Some people set a username PIN (4 digits, or 6 letters and digits) that strangers must give to find them.
Strict format
3 to 35 characters of letters, digits, . and _, with at least one letter, no leading, trailing or double dot, no www. or .com, and none of the Meta brand names.

Mossmoon links your customer’s own WhatsApp as a linked device, so the line can find people by username exactly like the “new chat” search on their phone. If you’re weighing that against Meta’s platform, the Business API vs personal API explainer covers the trade-offs.

How do I send a WhatsApp message to an @username?

Use the normal send endpoint and swap the recipient field. Give exactly one of to (a phone number), to_wa_id (a WhatsApp id) or to_username; sending more than one is a 400.

  1. 1

    Link a WhatsApp number

    Create a line with POST /api/v1/wa/lines and have your customer scan the QR on its connect_url from WhatsApp’s Linked devices screen. The line is ready when line.ready arrives. Personal WhatsApp and the WhatsApp Business app both work.
  2. 2

    Get the username, and the PIN if there is one

    The person gives you their @username, in a form, on a call or on a card. If they set a username PIN, they need to give you that as well.
  3. 3

    Send with to_username

    POST /lines/:id/send with to_username (and username_pin when given) in place of to. Text and media both work.
    curl https://mossmoon.app/api/v1/wa/lines/wa_8f3c2e1a/send \
      -H "Authorization: Bearer mm_live_..." \
      -H "Content-Type: application/json" \
      -d '{
        "to_username": "@sarah.k",
        "username_pin": "4821",
        "text": "Hi Sarah, this is Anna from the open house."
      }'
  4. 4

    Save to_wa_id and use it from now on

    The response adds to_wa_id, the person’s WhatsApp id. Store it on the contact and send follow-ups with to_wa_id. Their replies arrive with the same value in from_wa_id, so the thread lines up.

A username that can’t be resolved is refused before anything is sent and doesn’t count against the line’s daily send limit. The full contract is in the docs under WhatsApp usernames and send a message.

How do I look up a WhatsApp username without messaging them?

POST /lines/:id/usernames/lookup finds the account behind a username and sends nothing. The person is not notified. It is a POST so a PIN never ends up in a URL or an access log.

Look up a username
POST /api/v1/wa/lines/wa_8f3c2e1a/usernames/lookup
Authorization: Bearer mm_live_...
Content-Type: application/json

{ "username": "@sarah.k", "username_pin": "4821" }

→ 200 OK
{
  "line_id": "wa_8f3c2e1a",
  "wa_id": "777000111222333@lid",
  "username": "@sarah.k",
  "is_business": false,
  "business_name": null,
  "phone_e164": null,
  "is_me": false,
  "source": "whatsapp"
}
  • wa_id is the person’s WhatsApp id. Use it as to_wa_id anywhere in the API.
  • phone_e164 is set only when WhatsApp itself shows a number. For someone who shared just a username, expect null.
  • is_business and business_name tell you whether you’re about to message a company, and is_me flags the line’s own username.
  • source is "whatsapp", or "local" when the line already knew the account.

A lookup can also fail in a handful of well-defined ways, and each has its own error code:

Six lookups and what each one returns. Send by username fails with the same errors, before anything is sent.

One more: 503 username_lookup_failed means WhatsApp didn’t answer, and it is safe to retry. A 404 doesn’t always mean the name is free; a username that is only reserved can’t be found yet.

What is a WhatsApp username PIN, and how do I pass it?

The PIN is WhatsApp’s guard against strangers. If a person turned it on, nobody can find them by username alone, through the phone or through an API. Pass it as username_pin on both send and lookup. It is 4 digits, or 6 letters and digits; anything else is refused with a 400 before WhatsApp is asked.

Collect the PIN next to the username, as an optional field. Your app sends both in one request.

Ask for the PIN in the same place you collect the username, and store it with the contact only as long as you need it. Treat 403 username_pin_wrong as a reason to ask the person again, never as a cue to try other codes: WhatsApp limits PIN attempts. 429 username_pin_unavailable means WhatsApp says the account can’t be reached by PIN right now.

How do usernames arrive on webhooks?

Every message from someone with a username carries it in from_username. For a person reachable only by username, from_e164 is null, addressing is "lid" and from_wa_id ends in @lid.

Webhook: message.received from a username-only contact
POST https://your-app.com/webhooks/whatsapp
X-Mossmoon-Event: message.received

{
  "event": "message.received",
  "line_id": "wa_8f3c2e1a",
  "ts": "2026-10-16T10:05:40.118Z",
  "data": {
    "message_id": "wamsg_xyz",
    "from_me": false,
    "from": "777000111222333@lid",
    "from_e164": null,
    "from_wa_id": "777000111222333@lid",
    "from_name": "Sarah K",
    "from_username": "@sarah.k",
    "addressing": "lid",
    "is_group": false,
    "author_username": null,
    "text": "Thanks! Is Saturday 11:00 still free?",
    "type": "chat"
  }
}
  • New contacts. The first time someone writes, contact.first_seen fires once with their profile, including username and phone_e164 (null when WhatsApp doesn’t share it). See contact.first_seen.
  • Groups. The member who wrote a group message comes with author_username, and author_e164 is null for members known only by username.
  • Your own line. GET /lines/:id/profile returns the linked account’s username (set on the phone), or null.

Replying needs no special case. Prefer to: from_e164 when it is set, and fall back to to_wa_id: from_wa_id when it is null:

Reply handler (Node.js)
// One reply shape for every contact
const d = event.data;
const target = d.from_e164 ? { to: d.from_e164 } : { to_wa_id: d.from_wa_id };

await fetch(`https://mossmoon.app/api/v1/wa/lines/${event.line_id}/send`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.MOSSMOON_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ ...target, text: "Saturday 11:00 is yours." }),
});

If you’re new to receiving events, the webhook setup guide covers signatures, retries and idempotency.

How should a CRM store WhatsApp username contacts?

Most CRMs assume every contact has a phone number. Usernames break that assumption, so the contact record needs a different primary key.

Two people writing to the same line. Only one of them has a phone number your CRM can see.
  • Key every WhatsApp contact on from_wa_id. Same person, same wa_id, so it is your dedup key. Phone-number contacts have one too.
  • Keep the phone field optional. Fill it from from_e164 when it is present. Never derive a number from the digits of an @lid id.
  • Show the username as the handle. Store from_username for display and search, so your team sees @sarah.k instead of a long id.
  • Match other channels carefully. Cross-channel matching (SMS, email) works only when a phone number is present. For username-only contacts, link records by hand or by something the person tells you.

The background on why WhatsApp ids and phone numbers differ is in the docs under contact identity.

Can I add someone to a WhatsApp group by username?

Yes. On POST /groups/:group_id/participants, a participant can be "@sarah.k", or "@sarah.k:4821" with their PIN. The line finds them the way WhatsApp’s own “Add member” search does. Their entry comes back with username, an @lid wa_id and e164: null.

Add by number and by username
POST /api/v1/wa/lines/wa_8f3c2e1a/groups/[email protected]/participants
Authorization: Bearer mm_live_...

{ "participants": ["+15553182290", "@sarah.k:4821"] }

If a username can’t be resolved, nothing is added and the call fails with that lookup’s error, naming the username. To @mention a username-only member, pass their wa_id in mentions and put @ plus its digits in the text; WhatsApp shows their name in the tag. Removing, promoting and demoting take the wa_id. More in the groups API guide.

What are the limits of the WhatsApp usernames API?

  • 10 lookups a minute per line. That counts lookups that reach WhatsApp, and WhatsApp has its own limit on top. Both return 429 username_lookup_rate_limited with retry_after_s.
  • No scraping. Lookups are a query from your customer’s own WhatsApp account. Use them when someone gives you a username, not to test lists of names; WhatsApp treats that as scraping.
  • Still rolling out. Not every account has a username, and reserved ones can’t be found yet, so keep phone numbers as the default route.
  • PIN attempts are limited by WhatsApp. Ask; don’t retry with guesses.
  • Normal send caps apply once a username resolves: a soft cap of 750 and a hard cap of 1,500 sends a day on warmed lines (rate limits).

Mossmoon vs goghl.ai for WhatsApp usernames

goghl.ai also links WhatsApp by QR, inside GoHighLevel. It handles people who write in by username: it creates the contact with an empty phone field, routes replies by their WhatsApp id and includes the username on its webhooks, so none of that is listed here. These are the username features we offer that their public docs don’t list.

Mossmoon

$15 a line. First line free for 7 days.

Start free →
Mossmoon vs goghl.ai
FeatureRecommendedMossmoon$15 a line. First line free for 7 days.Start free →goghl.ai
Start a new chat from an @usernameYesto_username on /send, text or mediaNot listedTheir docs cover username contacts created when the person writes first
Look up a username without messagingYesPOST /usernames/lookup returns the WhatsApp id; the person isn't notifiedNot listed
Username PINsYesusername_pin on send and lookup, with separate errors for a missing or wrong PINNot listed
Add group members by usernameYes“@name” or “@name:PIN” on /participantsNot listedTheir group action takes a phone number

Checked against goghl.ai’s public site and help center on October 16, 2026. “Not listed” means we couldn’t find it in their docs, not that it can’t exist. Only features goghl.ai doesn’t list are shown. Sources: goghl.ai usernames, webhooks, group participant action, goghl.ai.

Ready to try it?

Start free: your first line is free for 7 days, then $15 a month. Link a number, send to one username you were given, and watch the reply arrive with from_username.

Frequently asked questions


Yes. With Mossmoon, call POST /api/v1/wa/lines/:id/send with to_username (for example "@sarah.k") instead of a phone number, plus username_pin if the person set one. The line looks the username up the way WhatsApp's own new chat search does and sends text or media. The response includes to_wa_id, which you use for follow-ups.


POST /api/v1/wa/lines/:id/usernames/lookup with the username (and PIN if needed) returns the account's WhatsApp id, whether it is a business and its business name. Nothing is sent and the person is not notified.


Not as a rule. The lookup returns phone_e164 only when WhatsApp itself shows one. Someone who shared only a username usually has a WhatsApp id ending in @lid and no phone number, and the digits of that id are not a phone number.


An optional code (4 digits, or 6 letters and digits) a person can set so strangers can't find them by username alone. Pass it as username_pin. Without it you get 403 username_pin_required, and a wrong one gets 403 username_pin_wrong. WhatsApp limits PIN attempts, so never guess.


Mossmoon allows 10 lookups per minute per line that reach WhatsApp, and WhatsApp has its own limit on top. Over either one you get 429 username_lookup_rate_limited with retry_after_s. Look up usernames people give you; scanning lists of names is treated by WhatsApp as scraping.


Because WhatsApp didn't share that person's number, which is what happens when someone is reachable only by username. The message still carries from_wa_id (ending in @lid) and usually from_username. Key the contact on from_wa_id and reply with to_wa_id.



Reach people by @username from your own software.

Link a number with one QR scan, then send, look up and receive usernames over REST and webhooks. $15 per line a month, first line free for 7 days.

WhatsApp usernames API: message and look up @usernames