Wow! Build your WhatsApp integration. Start free. Register Now
WhatsApp API

Numbers that are not on WhatsApp stay unused

A lead who is not on WhatsApp is left out of the mailing and the CRM import. When a phone list is about to be used, Make or n8n can check that number and stop the next step through Whapi.Cloud. This use case shows integrators how to implement that flow through the WhatsApp API.

Mailing path can send the text Import path does not send
A desk scene with a phone list on a laptop, a WhatsApp chat that was not opened for a number left off the list, and a CRM row left blank for that number.
Decorartive bubble
Use case

When to use this flow

The list is about to be mailed or imported

Use this flow when the list is about to be mailed or imported from a CRM, ERP, or another service.

The next module is Make or n8n

Use this flow when Make or n8n would otherwise mail or import a number that is not on WhatsApp.

The list needs a number check first

Use this flow when you need a number check before the list is used, so a number that is not on WhatsApp is not mailed and is not imported.

A phone showing a WhatsApp chat list with no new thread for a number left off the mailing, beside a CRM contact list that does not include that number.
Customer experience

The lead is not contacted in vain

Before automation, a mailing still goes to numbers that are not on WhatsApp, and a CRM import keeps those numbers, so the lead is never reached and the imported list is dirty.

After the flow runs, a person who is not on WhatsApp does not get the mailing, and that number is not added to the CRM import. Integrators build this result with the WhatsApp API.

Workflow

Check the list, then send or stop

1

The list is about to be used

A phone list from a CRM, ERP, or another service is about to be mailed or imported in Make or n8n.

2

Check which numbers are on WhatsApp

POST /contacts

checkPhones checks the phones on the list. Pass those phones in contacts. HTTP 200 returns contacts. Each item has input and status. valid also includes wa_id. invalid means the number does not have WhatsApp. Do not mail that number, do not import it, and do not call sendMessageText for it.
{"contacts":[{"input":"15052462002","status":"valid","wa_id":"[email protected]"},{"input":"15058662314","status":"invalid"}]}

3

Send the text on the mailing path

POST /messages/text

sendMessageText runs only when the list is a mailing and checkPhones returned status valid for that number. Put that number in to. A contact with status invalid does not reach this step. A CRM import stops after the check and must not call this send.

Need delivery tracking? The mailing send response does not mean the person already has the text. Status changes arrive after that send. A CRM import has no send to track. Learn about delivery status →

Prerequisites

Before you run the flow

Connect the WhatsApp number that checks the list and sends the mailing text. Every request uses that number's API token. Make or n8n stays in your scenario. Whapi.Cloud runs the check and, on the mailing path, the text send.

Optional — go deeper for this flow

API implementation

Two calls for this gate

Check the phones on the list first. Send a text only when the list is a mailing and the number is on WhatsApp. A CRM import stops after the check.

Get your API token for free after you register on whapi.cloud, and use that token in the code instead of Your_Token from the examples.

Check which numbers are on WhatsApp

Read each number's status. Keep only valid for the next step. invalid means the number does not have WhatsApp, so do not mail it and do not import it.

curl --request POST \
     --url https://gate.whapi.cloud/contacts \
     --header 'accept: application/json' \
     --header 'authorization: Bearer Your_Token' \
     --header 'content-type: application/json' \
     --data '
{
  "contacts": ["15052462002", "15058662314"]
}
'
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://gate.whapi.cloud/contacts",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{  \"contacts\": [\"15052462002\", \"15058662314\"]}",
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "authorization: Bearer Your_Token",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
curl_close($curl);
echo $response;
import requests

url = "https://gate.whapi.cloud/contacts"
payload = {"contacts": ["15052462002", "15058662314"]}
headers = {
    "accept": "application/json",
    "authorization": "Bearer Your_Token",
    "content-type": "application/json"
}

response = requests.request("POST", url, json=payload, headers=headers)
print(response.text)
const response = await fetch("https://gate.whapi.cloud/contacts", {
  method: "POST",
  headers: {
    accept: "application/json",
    authorization: "Bearer Your_Token",
    "content-type": "application/json"
  },
  body: JSON.stringify({ contacts: ["15052462002", "15058662314"] })
});

console.log(await response.text());
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{  \"contacts\": [\"15052462002\", \"15058662314\"]}");
Request request = new Request.Builder()
  .url("https://gate.whapi.cloud/contacts")
  .method("POST", body)
  .addHeader("accept", "application/json")
  .addHeader("authorization", "Bearer Your_Token")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
using RestSharp;

var options = new RestClientOptions("https://gate.whapi.cloud/contacts");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("accept", "application/json");
request.AddHeader("authorization", "Bearer Your_Token");
request.AddHeader("content-type", "application/json");
request.AddJsonBody(new { contacts = new[] { "15052462002", "15058662314" } });
var response = await client.ExecuteAsync(request);

Console.WriteLine("{0}", response.Content);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	body := []byte(`{
  "contacts": ["15052462002", "15058662314"]
}`)
	req, _ := http.NewRequest("POST", "https://gate.whapi.cloud/contacts", bytes.NewReader(body))
	req.Header.Set("accept", "application/json")
	req.Header.Set("authorization", "Bearer Your_Token")
	req.Header.Set("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}

Send the text on the mailing path

Send the mailing text only to a number the check kept, and skip this call on a CRM import.

curl --request POST \
     --url https://gate.whapi.cloud/messages/text \
     --header 'accept: application/json' \
     --header 'authorization: Bearer Your_Token' \
     --header 'content-type: application/json' \
     --data '
{
  "to": "15052462002",
  "body": "Hello"
}
'
$curl = curl_init();

curl_setopt_array($curl, [
  CURLOPT_URL => "https://gate.whapi.cloud/messages/text",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{  \"to\": \"15052462002\",  \"body\": \"Hello\"}",
  CURLOPT_HTTPHEADER => [
    "accept: application/json",
    "authorization: Bearer Your_Token",
    "content-type: application/json"
  ],
]);

$response = curl_exec($curl);
curl_close($curl);
echo $response;
import requests

url = "https://gate.whapi.cloud/messages/text"
payload = {"to": "15052462002", "body": "Hello"}
headers = {
    "accept": "application/json",
    "authorization": "Bearer Your_Token",
    "content-type": "application/json"
}

response = requests.request("POST", url, json=payload, headers=headers)
print(response.text)
const response = await fetch("https://gate.whapi.cloud/messages/text", {
  method: "POST",
  headers: {
    accept: "application/json",
    authorization: "Bearer Your_Token",
    "content-type": "application/json"
  },
  body: JSON.stringify({ to: "15052462002", body: "Hello" })
});

console.log(await response.text());
OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{  \"to\": \"15052462002\",  \"body\": \"Hello\"}");
Request request = new Request.Builder()
  .url("https://gate.whapi.cloud/messages/text")
  .method("POST", body)
  .addHeader("accept", "application/json")
  .addHeader("authorization", "Bearer Your_Token")
  .addHeader("content-type", "application/json")
  .build();

Response response = client.newCall(request).execute();
using RestSharp;

var options = new RestClientOptions("https://gate.whapi.cloud/messages/text");
var client = new RestClient(options);
var request = new RestRequest("");
request.AddHeader("accept", "application/json");
request.AddHeader("authorization", "Bearer Your_Token");
request.AddHeader("content-type", "application/json");
request.AddJsonBody(new { to = "15052462002", body = "Hello" });
var response = await client.ExecuteAsync(request);

Console.WriteLine("{0}", response.Content);
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	body := []byte(`{
  "to": "15052462002",
  "body": "Hello"
}`)
	req, _ := http.NewRequest("POST", "https://gate.whapi.cloud/messages/text", bytes.NewReader(body))
	req.Header.Set("accept", "application/json")
	req.Header.Set("authorization", "Bearer Your_Token")
	req.Header.Set("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	out, _ := io.ReadAll(res.Body)
	fmt.Println(string(out))
}
Delivery

Status after the mailing send

This tracking is for the mailing text only. A CRM import does not send. A successful send response does not mean the recipient already has the text. The first status visible before a webhook is pending. Later status changes arrive only on statuses.post and statuses.put. If the status stays pending and does not move to sent, delivered, or a later ack, the message was not delivered. Check the payload and the account's permission to send that type.

  • A successful sendMessageText response includes sent. That does not mean the person already has the text.

  • Later ack changes arrive on statuses.post and statuses.put.

What to read on delivery and webhooks

Contact our support if something went wrong
When things break

Handle failures at each step

The check can show that a number is not on WhatsApp while the next Make or n8n step still sends the message or still imports that number. Stop the use at that step.

The check said no, but the next step still ran?

Stop that Make or n8n step. Do not send the message, and do not import the number. Clients often mention that our support is responsive. Do not hesitate to ask for help.

Contact our support if something went wrong

Troubleshoot by API step

Status What it means What to do
200 OK HTTP 200 returns contacts. valid includes wa_id. invalid means the number does not have WhatsApp, so do not mail it, do not import it, and do not call sendMessageText for it. Example: {"contacts":[{"input":"15052462002","status":"valid","wa_id":"[email protected]"},{"input":"15058662314","status":"invalid"}]}.
400 Wrong request parameters
401 Need channel authorization for check phones Authorize the channel before this check.
402 Trial version limit exceeded
429 Too many requests
500 Internal Error
Status What it means What to do
200 Created
400 Wrong request parameters
401 Need channel authorization for send message Authorize the channel before this send.
402 Trial version limit exceeded
403 It is forbidden to send to this group/recipient
413 Request body too large
429 Too many requests
500 Internal Error
FAQ

Questions operators ask

No. A CRM import stops after checkPhones and must not call sendMessageText. A text goes out only when the list is a mailing and the check says the number is on WhatsApp.
Stop that Make or n8n step. Do not send the message, and do not import the number.
No. The check only tells you whether the number is on WhatsApp. It does not record consent to receive the mailing.
From a CRM, ERP, or another service, into Make or n8n. Whapi.Cloud does not read that system. It checks the phones you pass in, then sends a text only on the mailing path.
So a number that is not on WhatsApp is not mailed and is not imported. The check is the gate on the list, not a separate consent step.
Supported WhatsApp Features

Everything you need to build WhatsApp automation

Whapi.Cloud gives you access to a complete set of WhatsApp features — messaging, media, groups, channels, webhooks and more. Build bots, automations and integrations without feature limitations.

Chosen by developers

Trusted by 3,300+ developers and SaaS teams

Developers worldwide rely on Whapi.Cloud to build automations, bots, CRM integrations, bulk messaging tools and advanced WhatsApp workflows. Our infrastructure processes millions of API requests with high stability — and grows every month.

Icon of feature

5,5K+

active API channels

Icon of feature

21M+

API requests processed monthly

“Everything is simple, intuitive and easy to navigate. The docs are clear, and all the info I need is right on the screen. Great job.”

roberto k. / Web Developer

“We connected WhatsApp to our workflows using n8n in just a few minutes. No code, no complications- everything worked on the first try.”

Martin p. / Developer

“Whapi.Cloud became a key part of our customer communication. We automated product updates, order notifications and support messages without needing Meta templates or approvals”

Oscar T. / CTO

Decorative bubble