Managing Contacts

Contacts are intelligent phone number records that automatically detect messaging capabilities and enable optimized delivery routing across SMS and WhatsApp.

Overview

A Contact in Sent represents more than just a phone number. It includes:

  • Validated phone number in multiple formats (E.164, International, National)
  • Channel availability - Which channels can reach this contact
  • Routing preferences - Optimal channel based on history
  • Engagement data - Delivery success patterns

Why Use Contacts?

❌ Without Contacts (Direct Phone Number)

// You handle routing logic
if (await checkWhatsApp(phoneNumber)) {
  await sendWhatsApp(phoneNumber, message);
} else {
  await sendSMS(phoneNumber, message);
}

✅ With Contacts

// Sent handles routing automatically using the phone number
// Contacts are created/updated behind the scenes
await sent.messages.send({
  to: [contact.phoneNumber],
  template: { id: templateId }
});

Creating Contacts

Automatic Creation

Contacts are created automatically when you send to a new phone number:

# Contact will be created automatically
curl -X POST "https://api.sent.dm/v3/messages" \
  -H "x-api-key: $SENT_API_KEY" \
  -d '{
    "to": ["+1234567890"],
    "template": {
      "id": "tmpl_123"
    },
    "test_mode": false
  }'
// First message creates the contact automatically
const response = await client.messages.send({
  to: ['+1234567890'],
  template: { id: 'tmpl_123' }
});

// Response includes message_id for tracking
console.log(response.data.recipients[0].message_id);
# First message creates the contact automatically
response = client.messages.send(
    to=["+1234567890"],
    template={"id": "tmpl_123"}
)

# Response includes message_id for tracking
print(response.data.recipients[0].message_id)
// First message creates the contact automatically
response, err := client.Messages.Send(context.Background(), sentdm.MessageSendParams{
    To: []string{"+1234567890"},
    Template: sentdm.MessageSendParamsTemplate{
        ID: sentdm.String("tmpl_123"),
    },
})

// Response includes message_id for tracking
fmt.Println(response.Data.Recipients[0].MessageID)
// First message creates the contact automatically
MessageSendParams params = MessageSendParams.builder()
    .addTo("+1234567890")
    .template(MessageSendParams.Template.builder()
        .id("tmpl_123")
        .build())
    .build();

var response = client.messages().send(params);
// Response includes message_id for tracking
System.out.println(response.data().recipients().get(0).messageId());
// First message creates the contact automatically
MessageSendParams parameters = new()
{
    To = new List<string> { "+1234567890" },
    Template = new MessageSendParamsTemplate { Id = "tmpl_123" }
};

var response = await client.Messages.SendAsync(parameters);
// Response includes message_id for tracking
Console.WriteLine(response.Data.Recipients[0].MessageId);
// First message creates the contact automatically
$result = $client->messages->send(
    to: ['+1234567890'],
    template: ['id' => 'tmpl_123']
);

// Response includes message_id for tracking
echo $result->data->recipients[0]->message_id;
# First message creates the contact automatically
result = sent_dm.messages.send(
  to: ["+1234567890"],
  template: { id: "tmpl_123" }
)

# Response includes message_id for tracking
puts result.data.recipients[0].message_id

Explicit Creation

Create contacts before sending for better organization:

curl -X POST "https://api.sent.dm/v3/contacts" \
  -H "x-api-key: $SENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+1234567890"
  }'
const contact = await client.contacts.create({
  phoneNumber: '+1234567890'
});

console.log(`Contact created: ${contact.data.id}`);
contact = client.contacts.create(
    phone_number="+1234567890"
)

print(f"Contact created: {contact.data.id}")
contact, err := client.Contacts.Create(context.Background(), sentdm.ContactCreateParams{
    PhoneNumber: sentdm.String("+1234567890"),
})

fmt.Println("Contact created:", contact.Data.ID)
ContactCreateParams params = ContactCreateParams.builder()
    .phoneNumber("+1234567890")
    .build();

var contact = client.contacts().create(params);
System.out.println("Contact created: " + contact.data().id());
ContactCreateParams parameters = new()
{
    PhoneNumber = "+1234567890"
};

var contact = await client.Contacts.CreateAsync(parameters);
Console.WriteLine($"Contact created: {contact.Data.Id}");
$contact = $client->contacts->create(
    phoneNumber: '+1234567890'
);

echo "Contact created: " . $contact->data->id . "\n";
contact = sent_dm.contacts.create(
  phone_number: "+1234567890"
)

puts "Contact created: #{contact.data.id}"

Contact Lifecycle

Retrieving Contacts

List All Contacts

# List with pagination
curl "https://api.sent.dm/v3/contacts?page=1&pageSize=100" \
  -H "x-api-key: $SENT_API_KEY"

# Search contacts
curl "https://api.sent.dm/v3/contacts?page=1&pageSize=100&search=+1234" \
  -H "x-api-key: $SENT_API_KEY"
// List all contacts
const contacts = await client.contacts.list({
  page: 1,
  pageSize: 100
});

// Search contacts
const searchResults = await client.contacts.list({
  search: '+1234'
});

// Iterate through all pages
for (const contact of contacts.data) {
  console.log(`${contact.phoneNumber} - ${contact.availableChannels}`);
}
# List all contacts
contacts = client.contacts.list(page=1, page_size=100)

# Search contacts
search_results = client.contacts.list(search="+1234")

# Iterate through results
for contact in contacts.data:
    print(f"{contact.phone_number} - {contact.available_channels}")
// List all contacts
contacts, err := client.Contacts.List(context.Background(), sentdm.ContactListParams{
    Page:     sentdm.Int(1),
    PageSize: sentdm.Int(100),
})

// Search contacts
searchResults, err := client.Contacts.List(context.Background(), sentdm.ContactListParams{
    Search: sentdm.String("+1234"),
})

// Iterate through results
for _, contact := range contacts.Data {
    fmt.Printf("%s - %s\n", contact.PhoneNumber, contact.AvailableChannels)
}
// List all contacts
ContactListParams params = ContactListParams.builder()
    .page(1)
    .pageSize(100)
    .build();

var contacts = client.contacts().list(params);

// Search contacts
var searchResults = client.contacts().list(ContactListParams.builder()
    .search("+1234")
    .build());

// Iterate through results
for (var contact : contacts.data()) {
    System.out.println(contact.phoneNumber() + " - " + contact.availableChannels());
}
// List all contacts
ContactListParams parameters = new()
{
    Page = 1,
    PageSize = 100
};

var contacts = await client.Contacts.ListAsync(parameters);

// Search contacts
var searchResults = await client.Contacts.ListAsync(new ContactListParams
{
    Search = "+1234"
});

// Iterate through results
foreach (var contact in contacts.Data)
{
    Console.WriteLine($"{contact.PhoneNumber} - {contact.AvailableChannels}");
}
// List all contacts
$contacts = $client->contacts->list(page: 1, pageSize: 100);

// Search contacts
$searchResults = $client->contacts->list(search: '+1234');

// Iterate through results
foreach ($contacts->data as $contact) {
    echo "{$contact->phone_number} - {$contact->available_channels}\n";
}
# List all contacts
contacts = sent_dm.contacts.list(page: 1, page_size: 100)

# Search contacts
search_results = sent_dm.contacts.list(search: "+1234")

# Iterate through results
contacts.data.each do |contact|
  puts "#{contact.phone_number} - #{contact.available_channels}"
end

Get Single Contact

curl "https://api.sent.dm/v3/contacts/contact_1234567890" \
  -H "x-api-key: $SENT_API_KEY"
const contact = await client.contacts.get('contact_1234567890');

console.log('Phone:', contact.data.phoneNumber);
console.log('Channels:', contact.data.availableChannels);
console.log('Default Channel:', contact.data.defaultChannel);
contact = client.contacts.get("contact_1234567890")

print(f"Phone: {contact.data.phone_number}")
print(f"Channels: {contact.data.available_channels}")
print(f"Default: {contact.data.default_channel}")
contact, err := client.Contacts.Get(context.Background(), "contact_1234567890")

fmt.Println("Phone:", contact.Data.PhoneNumber)
fmt.Println("Channels:", contact.Data.AvailableChannels)
fmt.Println("Default:", contact.Data.DefaultChannel)
var contact = client.contacts().get("contact_1234567890");

System.out.println("Phone: " + contact.data().phoneNumber());
System.out.println("Channels: " + contact.data().availableChannels());
System.out.println("Default: " + contact.data().defaultChannel());
var contact = await client.Contacts.GetAsync("contact_1234567890");

Console.WriteLine($"Phone: {contact.Data.PhoneNumber}");
Console.WriteLine($"Channels: {contact.Data.AvailableChannels}");
Console.WriteLine($"Default: {contact.Data.DefaultChannel}");
$contact = $client->contacts->get("contact_1234567890");

echo "Phone: {$contact->data->phone_number}\n";
echo "Channels: {$contact->data->available_channels}\n";
echo "Default: {$contact->data->default_channel}\n";
contact = sent_dm.contacts.get("contact_1234567890")

puts "Phone: #{contact.data.phone_number}"
puts "Channels: #{contact.data.available_channels}"
puts "Default: #{contact.data.default_channel}"

Response Structure

{
  "success": true,
  "data": {
    "id": "contact_1234567890",
    "phone_number": "+1234567890",
    "format_e164": "+1234567890",
    "format_international": "+1 234-567-890",
    "format_national": "(234) 567-890",
    "format_rfc": "tel:+1-234-567-890",
    "country_code": "1",
    "region_code": "US",
    "available_channels": "sms,whatsapp",
    "default_channel": "whatsapp",
    "opt_out": false,
    "is_inherited": false,
    "created_at": "2025-01-01T12:00:00Z",
    "updated_at": "2025-01-15T08:30:00Z"
  },
  "error": null,
  "meta": {
    "request_id": "req_contact_001",
    "timestamp": "2026-03-04T11:28:25.2096416+00:00",
    "version": "v3"
  }
}

Updating Contacts

curl -X PATCH "https://api.sent.dm/v3/contacts/contact_1234567890" \
  -H "x-api-key: $SENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "default_channel": "whatsapp",
    "opt_out": false
  }'
const updated = await client.contacts.update('contact_1234567890', {
  defaultChannel: 'whatsapp',
  optOut: false
});
updated = client.contacts.update(
    "contact_1234567890",
    default_channel="whatsapp",
    opt_out=False
)
updated, err := client.Contacts.Update(context.Background(), "contact_1234567890", sentdm.ContactUpdateParams{
    DefaultChannel: sentdm.String("whatsapp"),
    OptOut:         sentdm.Bool(false),
})
ContactUpdateParams params = ContactUpdateParams.builder()
    .defaultChannel("whatsapp")
    .optOut(false)
    .build();

var updated = client.contacts().update("contact_1234567890", params);
ContactUpdateParams parameters = new()
{
    DefaultChannel = "whatsapp",
    OptOut = false
};

var updated = await client.Contacts.UpdateAsync("contact_1234567890", parameters);
$updated = $client->contacts->update(
    "contact_1234567890",
    defaultChannel: 'whatsapp',
    optOut: false
);
updated = sent_dm.contacts.update(
  "contact_1234567890",
  default_channel: "whatsapp",
  opt_out: false
)

Deleting Contacts

curl -X DELETE "https://api.sent.dm/v3/contacts/contact_1234567890" \
  -H "x-api-key: $SENT_API_KEY"
await client.contacts.delete('contact_1234567890');
client.contacts.delete("contact_1234567890")
err := client.Contacts.Delete(context.Background(), "contact_1234567890")
client.contacts().delete("contact_1234567890");
await client.Contacts.DeleteAsync("contact_1234567890");
$client->contacts->delete("contact_1234567890");
sent_dm.contacts.delete("contact_1234567890")

Deleting a contact is permanent. Messages already sent to this contact will retain their delivery records, but you won't be able to reference the contact in future API calls.

Bulk Import

For importing large numbers of contacts, see the Batch Operations guide.

Best Practices

1. Store Contact IDs in Your Database

// Store the phone number for messaging
const user = await db.users.create({
  email: 'john@example.com',
  phoneNumber: '+1234567890'
});

// Send directly to the phone number
// Contacts are automatically created/updated by the API
await client.messages.send({
  to: [user.phoneNumber],
  template: { id: 'welcome' }
});

2. Respect Channel Preferences

const contact = await client.contacts.get(contactId);

// Check available channels before sending
if (!contact.data.availableChannels.includes('whatsapp')) {
  console.log('WhatsApp not available, will use SMS fallback');
}

4. Handle Validation Errors

try {
  const contact = await client.contacts.create({
    phoneNumber: 'invalid-number'
  });
} catch (error) {
  if (error.code === 'VALIDATION_002') {
    // Prompt user to correct their number
    showValidationError('Please enter a valid phone number');
  }
}

Phone Number Formats

Sent automatically normalizes phone numbers:

Input FormatNormalized (E.164)Display Format
+1 234-567-8900+12345678900+1 234-567-8900
(234) 567-8900+12345678900+1 234-567-8900
234-567-8900+12345678900+1 234-567-8900

Always include country codes. Numbers without country codes will be rejected or may be misrouted.

Contact Metadata

Store custom data with contacts:

await client.contacts.update('contact_123', {
  metadata: {
    customerSince: '2024-01-15',
    preferredLanguage: 'es',
    lastOrderId: 'order_456'
  }
});

Next Steps


On this page