How Contacts Work
When you send a message to a new recipient, Zavu automatically:- Creates a contact record
- Detects the country from the phone number
- Determines available messaging channels
- Captures the WhatsApp profile name (if available)
// Sending to a new recipient creates a contact
const message = await zavu.messages.send({
to: "+14155551234",
text: "Welcome!",
});
// Contact is now available
const contact = await zavu.contacts.getByPhone("+14155551234");
console.log(contact.id); // "con_abc123"
Contact Properties
| Property | Description |
|---|---|
id | Unique contact identifier |
phoneNumber | E.164 formatted phone number |
countryCode | Two-letter country code (e.g., “US”) |
profileName | WhatsApp display name (if available) |
availableChannels | Channels this contact can receive (sms, whatsapp, etc.) |
defaultChannel | Preferred channel for auto-routing |
verified | Whether the contact has been verified |
metadata | Custom key-value data |
Get Contact by ID
import Zavudev from '@zavudev/sdk';
const zavu = new Zavudev();
const contact = await zavu.contacts.get("con_abc123");
console.log(`Phone: ${contact.phoneNumber}`);
console.log(`Country: ${contact.countryCode}`);
console.log(`Channels: ${contact.availableChannels.join(", ")}`);
console.log(`Profile: ${contact.profileName || "Unknown"}`);
from zavudev import Zavudev
zavu = Zavudev()
contact = zavu.contacts.get("con_abc123")
print(f"Phone: {contact.phone_number}")
print(f"Country: {contact.country_code}")
print(f"Channels: {', '.join(contact.available_channels)}")
print(f"Profile: {contact.profile_name or 'Unknown'}")
require "zavudev"
client = Zavudev::Client.new(api_key: ENV["ZAVUDEV_API_KEY"])
contact = client.contacts.get("con_abc123")
puts "Phone: #{contact.phone_number}"
puts "Country: #{contact.country_code}"
puts "Channels: #{contact.available_channels.join(', ')}"
puts "Profile: #{contact.profile_name || 'Unknown'}"
contact, _ := client.Contacts.Get(context.TODO(), "con_abc123")
fmt.Println("Phone:", contact.PhoneNumber)
fmt.Println("Country:", contact.CountryCode)
fmt.Println("Channels:", contact.AvailableChannels)
fmt.Println("Profile:", contact.ProfileName)
<?php
$contact = $client->contacts->get('con_abc123');
echo "Phone: " . $contact->phoneNumber . "\n";
echo "Country: " . $contact->countryCode . "\n";
echo "Channels: " . implode(', ', $contact->availableChannels) . "\n";
echo "Profile: " . ($contact->profileName ?? 'Unknown') . "\n";
curl https://api.zavu.dev/v1/contacts/{contactId} \
-H "Authorization: Bearer $ZAVU_API_KEY"
Response
{
"id": "con_abc123",
"phoneNumber": "+14155551234",
"countryCode": "US",
"profileName": "John Doe",
"availableChannels": ["sms", "whatsapp"],
"defaultChannel": "whatsapp",
"verified": true,
"metadata": {
"tier": "premium",
"source": "website"
},
"createdAt": "2024-01-15T10:30:00.000Z",
"updatedAt": "2024-01-15T12:00:00.000Z"
}
Get Contact by Phone Number
Look up a contact using their phone number:const contact = await zavu.contacts.getByPhone("+14155551234");
if (contact) {
console.log(`Found contact: ${contact.id}`);
console.log(`Available channels: ${contact.availableChannels}`);
} else {
console.log("Contact not found");
}
contact = zavu.contacts.get_by_phone("+14155551234")
if contact:
print(f"Found contact: {contact.id}")
print(f"Available channels: {contact.available_channels}")
else:
print("Contact not found")
contact = client.contacts.get_by_phone("+14155551234")
if contact
puts "Found contact: #{contact.id}"
puts "Available channels: #{contact.available_channels}"
else
puts "Contact not found"
end
contact, err := client.Contacts.GetByPhone(context.TODO(), "+14155551234")
if err != nil {
fmt.Println("Contact not found")
} else {
fmt.Println("Found contact:", contact.ID)
fmt.Println("Available channels:", contact.AvailableChannels)
}
<?php
$contact = $client->contacts->getByPhone('+14155551234');
if ($contact) {
echo "Found contact: " . $contact->id . "\n";
echo "Available channels: " . implode(', ', $contact->availableChannels) . "\n";
} else {
echo "Contact not found\n";
}
# URL-encode the phone number (+14155551234 becomes %2B14155551234)
curl https://api.zavu.dev/v1/contacts/phone/%2B14155551234 \
-H "Authorization: Bearer $ZAVU_API_KEY"
Use this endpoint to check if a recipient already exists before sending messages, or to retrieve their metadata for personalization.
List Contacts
Retrieve all contacts with pagination:// List all contacts
const { items, nextCursor } = await zavu.contacts.list({
limit: 50,
});
for (const contact of items) {
console.log(`${contact.id}: ${contact.phoneNumber}`);
}
// Paginate through all contacts
let cursor: string | undefined;
const allContacts = [];
do {
const result = await zavu.contacts.list({ cursor, limit: 100 });
allContacts.push(...result.items);
cursor = result.nextCursor ?? undefined;
} while (cursor);
console.log(`Total contacts: ${allContacts.length}`);
# List all contacts
result = zavu.contacts.list(limit=50)
for contact in result.items:
print(f"{contact.id}: {contact.phone_number}")
# Paginate through all contacts
cursor = None
all_contacts = []
while True:
result = zavu.contacts.list(cursor=cursor, limit=100)
all_contacts.extend(result.items)
cursor = result.next_cursor
if not cursor:
break
print(f"Total contacts: {len(all_contacts)}")
# List all contacts
result = client.contacts.list(limit: 50)
result.items.each do |contact|
puts "#{contact.id}: #{contact.phone_number}"
end
# Paginate through all contacts
cursor = nil
all_contacts = []
loop do
result = client.contacts.list(cursor: cursor, limit: 100)
all_contacts.concat(result.items)
cursor = result.next_cursor
break unless cursor
end
puts "Total contacts: #{all_contacts.length}"
// List all contacts
result, _ := client.Contacts.List(context.TODO(), zavudev.ContactListParams{
Limit: zavudev.Int(50),
})
for _, contact := range result.Items {
fmt.Printf("%s: %s\n", contact.ID, contact.PhoneNumber)
}
<?php
// List all contacts
$result = $client->contacts->list(['limit' => 50]);
foreach ($result->items as $contact) {
echo "{$contact->id}: {$contact->phoneNumber}\n";
}
// Paginate through all contacts
$cursor = null;
$allContacts = [];
do {
$result = $client->contacts->list(['cursor' => $cursor, 'limit' => 100]);
$allContacts = array_merge($allContacts, $result->items);
$cursor = $result->nextCursor;
} while ($cursor);
echo "Total contacts: " . count($allContacts) . "\n";
curl "https://api.zavu.dev/v1/contacts?limit=50" \
-H "Authorization: Bearer $ZAVU_API_KEY"
Filter by Phone Number
Search for contacts matching a phone number prefix:// Find all contacts with US country code
const { items } = await zavu.contacts.list({
phoneNumber: "+1",
limit: 50,
});
Update Contact
Update a contact’s default channel or metadata:const contact = await zavu.contacts.update("con_abc123", {
defaultChannel: "whatsapp",
metadata: {
name: "John Doe",
tier: "premium",
source: "mobile_app",
},
});
console.log(`Updated default channel: ${contact.defaultChannel}`);
contact = zavu.contacts.update(
"con_abc123",
default_channel="whatsapp",
metadata={
"name": "John Doe",
"tier": "premium",
"source": "mobile_app"
}
)
print(f"Updated default channel: {contact.default_channel}")
contact = client.contacts.update(
"con_abc123",
default_channel: "whatsapp",
metadata: {
"name" => "John Doe",
"tier" => "premium",
"source" => "mobile_app"
}
)
puts "Updated default channel: #{contact.default_channel}"
contact, _ := client.Contacts.Update(context.TODO(), "con_abc123", zavudev.ContactUpdateParams{
DefaultChannel: zavudev.String("whatsapp"),
Metadata: map[string]string{
"name": "John Doe",
"tier": "premium",
"source": "mobile_app",
},
})
fmt.Println("Updated default channel:", contact.DefaultChannel)
<?php
$contact = $client->contacts->update('con_abc123', [
'defaultChannel' => 'whatsapp',
'metadata' => [
'name' => 'John Doe',
'tier' => 'premium',
'source' => 'mobile_app',
],
]);
echo "Updated default channel: " . $contact->defaultChannel . "\n";
curl -X PATCH https://api.zavu.dev/v1/contacts/{contactId} \
-H "Authorization: Bearer $ZAVU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"defaultChannel": "whatsapp",
"metadata": {
"name": "John Doe",
"tier": "premium"
}
}'
Updateable Fields
| Field | Description |
|---|---|
defaultChannel | Preferred channel for smart routing (sms, whatsapp, telegram, email) |
metadata | Custom key-value pairs (merged with existing metadata) |
Setting
defaultChannel affects smart routing. When you send a message with channel: "auto", Zavu will prefer the contact’s default channel.Contact Metadata
Store custom data on contacts for personalization and segmentation:// Add customer data
await zavu.contacts.update("con_abc123", {
metadata: {
customerId: "cust_12345",
plan: "enterprise",
signupDate: "2024-01-15",
preferredLanguage: "es",
},
});
// Use in messages
const contact = await zavu.contacts.get("con_abc123");
const message = await zavu.messages.send({
to: contact.phoneNumber,
text: `Hola ${contact.metadata?.customerId}! Your ${contact.metadata?.plan} plan is active.`,
});
Metadata values must be strings. Store complex data as JSON strings if needed.
Available Channels
TheavailableChannels array indicates which channels can reach this contact:
| Channel | When Available |
|---|---|
sms | Valid mobile or landline number |
whatsapp | Contact has WhatsApp (detected on first message or reply) |
telegram | Contact has connected via Telegram bot |
email | Contact has email address on file |
const contact = await zavu.contacts.get("con_abc123");
if (contact.availableChannels.includes("whatsapp")) {
// Send via WhatsApp for richer experience
await zavu.messages.send({
to: contact.phoneNumber,
channel: "whatsapp",
text: "Hello via WhatsApp!",
});
} else {
// Fall back to SMS
await zavu.messages.send({
to: contact.phoneNumber,
channel: "sms",
text: "Hello via SMS!",
});
}
Phone Number Introspection
Validate a phone number and check available channels without creating a contact:const result = await zavu.introspect.phone({
phoneNumber: "+14155551234",
});
console.log(`Valid: ${result.validNumber}`);
console.log(`Country: ${result.countryCode}`);
console.log(`Format: ${result.nationalFormat}`);
console.log(`Type: ${result.lineType}`);
console.log(`Carrier: ${result.carrier?.name}`);
console.log(`Channels: ${result.availableChannels}`);
result = client.introspect.phone(phone_number: "+14155551234")
puts "Valid: #{result.valid_number}"
puts "Country: #{result.country_code}"
puts "Format: #{result.national_format}"
puts "Type: #{result.line_type}"
puts "Carrier: #{result.carrier&.name}"
puts "Channels: #{result.available_channels}"
result, _ := client.Introspect.Phone(context.TODO(), zavudev.PhoneIntrospectionParams{
PhoneNumber: zavudev.String("+14155551234"),
})
fmt.Println("Valid:", result.ValidNumber)
fmt.Println("Country:", result.CountryCode)
fmt.Println("Format:", result.NationalFormat)
fmt.Println("Type:", result.LineType)
fmt.Println("Channels:", result.AvailableChannels)
<?php
$result = $client->introspect->phone([
'phoneNumber' => '+14155551234',
]);
echo "Valid: " . ($result->validNumber ? 'true' : 'false') . "\n";
echo "Country: " . $result->countryCode . "\n";
echo "Format: " . $result->nationalFormat . "\n";
echo "Type: " . $result->lineType . "\n";
echo "Carrier: " . ($result->carrier->name ?? 'Unknown') . "\n";
echo "Channels: " . implode(', ', $result->availableChannels) . "\n";
curl -X POST https://api.zavu.dev/v1/introspect/phone \
-H "Authorization: Bearer $ZAVU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"phoneNumber": "+14155551234"
}'
Response
{
"phoneNumber": "+14155551234",
"countryCode": "US",
"validNumber": true,
"nationalFormat": "(415) 555-1234",
"lineType": "mobile",
"carrier": {
"name": "Verizon Wireless",
"type": "mobile"
},
"availableChannels": ["sms", "whatsapp"]
}
Use introspection to validate phone numbers before adding them to broadcasts or CRM systems.
Complete Example
import Zavudev from '@zavudev/sdk';
const zavu = new Zavudev();
async function processNewCustomer(phone: string, customerData: {
name: string;
email: string;
plan: string;
}) {
// 1. Validate the phone number
const introspection = await zavu.introspect.phone({ phoneNumber: phone });
if (!introspection.validNumber) {
throw new Error("Invalid phone number");
}
console.log(`Valid ${introspection.lineType} number from ${introspection.countryCode}`);
// 2. Send welcome message (creates contact automatically)
const message = await zavu.messages.send({
to: phone,
channel: "auto", // Smart routing based on available channels
text: `Welcome ${customerData.name}! Your ${customerData.plan} account is ready.`,
});
console.log(`Welcome message sent: ${message.id}`);
// 3. Update contact with customer data
const contact = await zavu.contacts.getByPhone(phone);
await zavu.contacts.update(contact.id, {
defaultChannel: introspection.availableChannels.includes("whatsapp")
? "whatsapp"
: "sms",
metadata: {
name: customerData.name,
email: customerData.email,
plan: customerData.plan,
signupDate: new Date().toISOString(),
},
});
console.log(`Contact updated: ${contact.id}`);
return contact;
}
// Usage
processNewCustomer("+14155551234", {
name: "John Doe",
email: "john@example.com",
plan: "premium",
});
Next Steps
Smart Routing
Learn how defaultChannel affects message routing
Broadcasts
Send messages to many contacts at once
Phone Lookup
Validate and lookup phone numbers
Webhooks
Receive updates when contacts reply
API Reference
Manage contacts programmatically:- List contacts — paginated list of contacts in your project
- Get a contact — retrieve a contact by ID
- Get contact by phone — lookup a contact by phone number
- Update a contact — modify name, attributes, or channels
- Delete a contact — permanently remove a contact
