Contacts represent email recipients in your Resend account. This guide covers creating, managing, and organizing contacts using both global contacts and audience-specific contacts.
Overview
The Contacts API supports two types of contacts:
Global Contacts Modern approach supporting custom properties and segment organization. Recommended for new integrations.
Audience-Specific Contacts Legacy approach tied to specific audiences. Limited property support.
Recommendation: Use global contacts for all new integrations. They provide more flexibility with custom properties and segment-based organization.
Feature Global Contacts Audience-Specific Custom Properties ✅ Yes ❌ No Segment Organization ✅ Yes ❌ No Topic Subscriptions ✅ Yes ✅ Yes API Path /contacts/audiences/{id}/contacts
Create a contact without an audience ID to make it globally available.
Basic Global Contact
With Custom Properties
import (
" github.com/resend/resend-go/v3 "
)
client := resend . NewClient ( "re_123456789" )
params := & resend . CreateContactRequest {
Email : "[email protected] " ,
FirstName : "John" ,
LastName : "Doe" ,
}
contact , err := client . Contacts . Create ( params )
if err != nil {
panic ( err )
}
fmt . Println ( "Contact ID:" , contact . Id )
Important: The Resend API currently only accepts string values for custom properties. Non-string values (numbers, booleans, etc.) will be rejected.// ✅ Correct
Properties : map [ string ] any {
"age" : "30" , // String
"active" : "true" , // String
}
// ❌ Incorrect
Properties : map [ string ] any {
"age" : 30 , // Number - will fail
"active" : true , // Boolean - will fail
}
Create a contact tied to a specific audience by providing an AudienceId.
Audience-Specific Contact
params := & resend . CreateContactRequest {
Email : "[email protected] " ,
AudienceId : "ca4e37c5-a82a-4199-a3b8-bf912a6472aa" ,
FirstName : "Jane" ,
LastName : "Smith" ,
Unsubscribed : false ,
}
contact , err := client . Contacts . Create ( params )
if err != nil {
panic ( err )
}
Custom Properties
Global contacts support custom properties for storing additional metadata. Properties must be defined before use.
Define Properties
First, create the property definitions you want to use: // Define available properties
properties := [] struct {
key string
typ string
}{
{ "tier" , "string" },
{ "role" , "string" },
{ "signup_source" , "string" },
}
for _ , prop := range properties {
_ , err := client . Contacts . Properties . Create ( & resend . CreateContactPropertyRequest {
Key : prop . key ,
Type : prop . typ ,
})
if err != nil {
fmt . Printf ( "Property ' %s ' may already exist: %v \n " , prop . key , err )
}
}
Use Properties on Contacts
Add the properties when creating or updating contacts: params := & resend . CreateContactRequest {
Email : "[email protected] " ,
FirstName : "John" ,
LastName : "Doe" ,
Properties : map [ string ] any {
"tier" : "premium" ,
"role" : "admin" ,
"signup_source" : "website" ,
},
}
contact , err := client . Contacts . Create ( params )
Access Properties
Properties are returned when retrieving contacts: contact , err := client . Contacts . Get ( & resend . GetContactOptions {
Id : contactId ,
})
if contact . Properties != nil {
fmt . Printf ( "Tier: %v \n " , contact . Properties [ "tier" ])
fmt . Printf ( "Role: %v \n " , contact . Properties [ "role" ])
}
Get a single contact by ID or email address.
Get Global Contact by ID
Get by Email Address
Get Audience-Specific Contact
options := & resend . GetContactOptions {
Id : "479e3145-dd38-476b-932c-529ceb705947" ,
}
contact , err := client . Contacts . Get ( options )
if err != nil {
panic ( err )
}
fmt . Printf ( " %s %s ( %s ) \n " , contact . FirstName , contact . LastName , contact . Email )
You can retrieve contacts by either their ID or email address. Both work interchangeably in the Id field.
Retrieve all contacts with optional pagination.
List Global Contacts
List Audience-Specific Contacts
With Pagination
// Omit AudienceId for global contacts
options := & resend . ListContactsOptions {}
contacts , err := client . Contacts . List ( options )
if err != nil {
panic ( err )
}
fmt . Printf ( "Found %d contacts \n " , len ( contacts . Data ))
for _ , contact := range contacts . Data {
fmt . Printf ( " %s ( %s ) \n " , contact . Email , contact . Id )
}
Update contact information including properties and subscription status.
Update Basic Info
Update Properties
Update Unsubscribed Status
params := & resend . UpdateContactRequest {
Id : "479e3145-dd38-476b-932c-529ceb705947" ,
FirstName : "Jane" ,
LastName : "Updated" ,
Email : "[email protected] " ,
}
updated , err := client . Contacts . Update ( params )
if err != nil {
panic ( err )
}
fmt . Println ( "Updated:" , updated . Data . Email )
The SetUnsubscribed() method is required to set the unsubscribed status to false due to Go’s zero-value behavior with booleans. This will be fixed in v3 of the SDK.
Remove contacts by ID or email address.
Remove Global Contact by ID
Remove by Email
Remove Audience-Specific Contact
options := & resend . RemoveContactOptions {
Id : "479e3145-dd38-476b-932c-529ceb705947" ,
}
removed , err := client . Contacts . Remove ( options )
if err != nil {
panic ( err )
}
fmt . Printf ( "Deleted: %v \n " , removed . Deleted )
Nested Services
The Contacts service includes three nested services for advanced functionality:
Topics
Manage contact topic subscriptions for preference-based email campaigns.
Managing Topic Subscriptions
// List topics for a contact
topics , err := client . Contacts . Topics . List ( contactId )
if err != nil {
panic ( err )
}
fmt . Printf ( "Contact has %d topic subscriptions \n " , len ( topics . Data ))
// Update topic subscriptions
updateParams := & resend . UpdateContactTopicsRequest {
Id : contactId ,
Topics : [] resend . TopicSubscriptionUpdate {
{
Id : "topic-123" ,
Subscription : "opt_in" ,
},
},
}
updated , err := client . Contacts . Topics . Update ( updateParams )
Segments
Add contacts to segments for targeted campaigns.
Managing Contact Segments
// Add contact to segment
addParams := & resend . AddContactSegmentRequest {
ContactId : contactId ,
SegmentId : "seg-123" ,
}
_ , err := client . Contacts . Segments . Add ( addParams )
if err != nil {
panic ( err )
}
// List contact's segments
listParams := & resend . ListContactSegmentsRequest {
ContactId : contactId ,
}
segments , err := client . Contacts . Segments . List ( listParams )
if err != nil {
panic ( err )
}
fmt . Printf ( "Contact is in %d segment(s) \n " , len ( segments . Data ))
// Remove contact from segment
removeParams := & resend . RemoveContactSegmentRequest {
ContactId : contactId ,
SegmentId : "seg-123" ,
}
removed , err := client . Contacts . Segments . Remove ( removeParams )
Properties
Define custom property schemas for contacts.
Managing Property Definitions
// Create a property definition
propParams := & resend . CreateContactPropertyRequest {
Key : "company_size" ,
Type : "string" ,
}
prop , err := client . Contacts . Properties . Create ( propParams )
if err != nil {
panic ( err )
}
// List all property definitions
properties , err := client . Contacts . Properties . List ()
if err != nil {
panic ( err )
}
for _ , prop := range properties . Data {
fmt . Printf ( " %s : %s \n " , prop . Key , prop . Type )
}
Complete Example
Here’s a complete workflow demonstrating contact management:
Complete Contact Workflow
package main
import (
" fmt "
" os "
" github.com/resend/resend-go/v3 "
)
func main () {
apiKey := os . Getenv ( "RESEND_API_KEY" )
client := resend . NewClient ( apiKey )
// 1. Define custom properties
properties := [] struct {
key string
typ string
}{
{ "tier" , "string" },
{ "role" , "string" },
{ "signup_source" , "string" },
}
for _ , prop := range properties {
_ , err := client . Contacts . Properties . Create ( & resend . CreateContactPropertyRequest {
Key : prop . key ,
Type : prop . typ ,
})
if err != nil {
fmt . Printf ( "Property ' %s ' may already exist \n " , prop . key )
}
}
// 2. Create a global contact with properties
createParams := & resend . CreateContactRequest {
Email : "[email protected] " ,
FirstName : "John" ,
LastName : "Doe" ,
Properties : map [ string ] any {
"tier" : "premium" ,
"role" : "admin" ,
"signup_source" : "website" ,
},
}
created , err := client . Contacts . Create ( createParams )
if err != nil {
panic ( err )
}
fmt . Printf ( "Created contact: %s \n " , created . Id )
// 3. Retrieve and display contact
contact , err := client . Contacts . Get ( & resend . GetContactOptions {
Id : created . Id ,
})
if err != nil {
panic ( err )
}
fmt . Printf ( " \n Contact Details: \n " )
fmt . Printf ( "Name: %s %s \n " , contact . FirstName , contact . LastName )
fmt . Printf ( "Email: %s \n " , contact . Email )
if contact . Properties != nil {
fmt . Printf ( "Properties: %+v \n " , contact . Properties )
}
// 4. Create a segment and add contact
segment , err := client . Segments . Create ( & resend . CreateSegmentRequest {
Name : "Premium Users" ,
})
if err != nil {
panic ( err )
}
_ , err = client . Contacts . Segments . Add ( & resend . AddContactSegmentRequest {
ContactId : created . Id ,
SegmentId : segment . Id ,
})
if err != nil {
panic ( err )
}
fmt . Println ( " \n Added contact to segment" )
// 5. List all global contacts
contacts , err := client . Contacts . List ( & resend . ListContactsOptions {})
if err != nil {
panic ( err )
}
fmt . Printf ( " \n Total global contacts: %d \n " , len ( contacts . Data ))
// 6. Update contact properties
updateParams := & resend . UpdateContactRequest {
Id : created . Id ,
Properties : map [ string ] any {
"tier" : "enterprise" ,
},
}
updated , err := client . Contacts . Update ( updateParams )
if err != nil {
panic ( err )
}
fmt . Printf ( " \n Updated contact tier: %v \n " , updated . Data . Properties [ "tier" ])
// 7. Clean up
_ , err = client . Contacts . Remove ( & resend . RemoveContactOptions {
Id : created . Id ,
})
if err != nil {
panic ( err )
}
fmt . Println ( " \n Contact removed" )
_ , err = client . Segments . Remove ( segment . Id )
if err != nil {
panic ( err )
}
fmt . Println ( "Segment removed" )
}
Best Practices
Use Global Contacts Prefer global contacts over audience-specific contacts for flexibility and custom properties support.
Define Properties First Always create property definitions before using them on contacts.
String Values Only Remember that custom properties currently only accept string values.
Organize with Segments Use segments instead of audiences to organize contacts into targeted groups.
Next Steps
Segments Learn how to organize contacts into segments
Broadcasts Send email campaigns to your contacts
Contact Properties View the contact properties example
API Reference View the complete Contacts API reference