> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zavu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Contacts

> Manage contacts and recipient information

Contacts are automatically created when you send messages. Each contact represents a unique recipient (phone number or email) and stores metadata about their messaging preferences and history.

## How Contacts Work

When you send a message to a new recipient, Zavu automatically:

1. Creates a contact record
2. Detects the country from the phone number
3. Determines available messaging channels
4. Captures the WhatsApp profile name (if available)

```typescript theme={null}
// 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

<CodeGroup>
  ```typescript TypeScript theme={null}
  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"}`);
  ```

  ```python Python theme={null}
  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'}")
  ```

  ```ruby Ruby theme={null}
  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'}"
  ```

  ```go Go theme={null}
  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 PHP theme={null}
  <?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";
  ```

  ```bash cURL theme={null}
  curl https://api.zavu.dev/v1/contacts/{contactId} \
    -H "Authorization: Bearer $ZAVU_API_KEY"
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "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:

<CodeGroup>
  ```typescript TypeScript theme={null}
  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");
  }
  ```

  ```python Python theme={null}
  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")
  ```

  ```ruby Ruby theme={null}
  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
  ```

  ```go Go theme={null}
  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 PHP theme={null}
  <?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";
  }
  ```

  ```bash cURL theme={null}
  # URL-encode the phone number (+14155551234 becomes %2B14155551234)
  curl https://api.zavu.dev/v1/contacts/phone/%2B14155551234 \
    -H "Authorization: Bearer $ZAVU_API_KEY"
  ```
</CodeGroup>

<Tip>
  Use this endpoint to check if a recipient already exists before sending messages, or to retrieve their metadata for personalization.
</Tip>

## List Contacts

Retrieve all contacts with pagination:

<CodeGroup>
  ```typescript TypeScript theme={null}
  // 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}`);
  ```

  ```python Python theme={null}
  # 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)}")
  ```

  ```ruby Ruby theme={null}
  # 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}"
  ```

  ```go Go theme={null}
  // 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 PHP theme={null}
  <?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";
  ```

  ```bash cURL theme={null}
  curl "https://api.zavu.dev/v1/contacts?limit=50" \
    -H "Authorization: Bearer $ZAVU_API_KEY"
  ```
</CodeGroup>

### Filter by Phone Number

Search for contacts matching a phone number prefix:

```typescript theme={null}
// 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:

<CodeGroup>
  ```typescript TypeScript theme={null}
  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}`);
  ```

  ```python Python theme={null}
  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}")
  ```

  ```ruby Ruby theme={null}
  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}"
  ```

  ```go Go theme={null}
  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 PHP theme={null}
  <?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";
  ```

  ```bash cURL theme={null}
  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"
      }
    }'
  ```
</CodeGroup>

### Updateable Fields

| Field            | Description                                                          |
| ---------------- | -------------------------------------------------------------------- |
| `defaultChannel` | Preferred channel for smart routing (sms, whatsapp, telegram, email) |
| `metadata`       | Custom key-value pairs (merged with existing metadata)               |

<Note>
  Setting `defaultChannel` affects [smart routing](/guides/sending-messages/smart-routing). When you send a message with `channel: "auto"`, Zavu will prefer the contact's default channel.
</Note>

## Contact Metadata

Store custom data on contacts for personalization and segmentation:

```typescript theme={null}
// 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.`,
});
```

<Info>
  Metadata values must be strings. Store complex data as JSON strings if needed.
</Info>

## Available Channels

The `availableChannels` 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                         |

```typescript theme={null}
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:

<CodeGroup>
  ```typescript TypeScript theme={null}
  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}`);
  ```

  ```ruby Ruby theme={null}
  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}"
  ```

  ```go Go theme={null}
  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 PHP theme={null}
  <?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";
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.zavu.dev/v1/introspect/phone \
    -H "Authorization: Bearer $ZAVU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "phoneNumber": "+14155551234"
    }'
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "phoneNumber": "+14155551234",
  "countryCode": "US",
  "validNumber": true,
  "nationalFormat": "(415) 555-1234",
  "lineType": "mobile",
  "carrier": {
    "name": "Verizon Wireless",
    "type": "mobile"
  },
  "availableChannels": ["sms", "whatsapp"]
}
```

<Tip>
  Use introspection to validate phone numbers before adding them to broadcasts or CRM systems.
</Tip>

## Complete Example

```typescript theme={null}
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

<CardGroup cols={2}>
  <Card title="Smart Routing" icon="route" href="/guides/sending-messages/smart-routing">
    Learn how defaultChannel affects message routing
  </Card>

  <Card title="Broadcasts" icon="bullhorn" href="/guides/broadcasts/overview">
    Send messages to many contacts at once
  </Card>

  <Card title="Phone Lookup" icon="magnifying-glass" href="/tools/number-lookup">
    Validate and lookup phone numbers
  </Card>

  <Card title="Webhooks" icon="webhook" href="/guides/receiving-messages/webhooks">
    Receive updates when contacts reply
  </Card>
</CardGroup>
