S
docs- Words
- 2,641
- Reading time
- 12 min
- Last crawled
- 2026-09-27
- Language
- EN
cat smashsend.com/docs/api/contacts.md
1. [ Home ](https://smashsend.com/) / 3. [ Docs ](https://smashsend.com/docs) / 5. [ API Reference ](https://smashsend.com/docs/api) / 7. [ Contacts ](https://smashsend.com/docs/api/contacts) The Contacts API allows you to manage contacts in your SMASHSEND account. This API is commonly used by Zapier developers and other integration platforms to sync contact data, create automated workflows, and manage customer information across different systems. ## Authentication & Base URL **Base URL:** `https://api.smashsend.com` All API requests must include your API key in the Authorization header. You can manage your API keys in your SMASHSEND dashboard under Settings → API. ```Authorization: Bearer sk_live_abc123...``` **Rate Limits:** API requests are limited to ensure service reliability. For Zapier integrations, we recommend using webhook triggers instead of polling for real-time updates. ## Contacts Endpoints ### GET/v1/contacts Lists contacts for the authenticated workspace with cursor-based pagination. Perfect for Zapier “New Contact” triggers and bulk contact synchronization. Request ```curl "https://api.smashsend.com/v1/contacts?limit=15&sort=createdAt.desc" \ -H "Authorization: Bearer YOUR_API_KEY"``` Response ```{ "contacts": { "cursor": "MjAyNS0wNi0wMlQwMDoyMDoxNS42OTVa", "hasMore": true, "items": [ { "id": "ctc_L10vZx0SFrmzKkHwWjoLYSIf", "createdAt": "2025-06-02T00:35:15.593Z", "updatedAt": "2025-06-04T04:13:20.114Z", "properties": { "avatarUrl": null, "birthday": null, "city": null, "countryCode": "DZ", "email": "contact@example.com", "firstName": "John", "language": "en", "lastName": "Doe", "phone": "", "status": "SUBSCRIBED", "timezone": "Africa/Algiers", "company": "ACME Corp" }, "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer" } ], "totalCount": 11 }}``` ### GET/v1/contacts/{contactId} Retrieves a specific contact by ID. Useful for Zapier “Find Contact” actions and getting detailed contact information. Request ```curl "https://api.smashsend.com/v1/contacts/ctc_abc123def456" \ -H "Authorization: Bearer YOUR_API_KEY"``` Response ```{ "contact": { "id": "ctc_E7g1zoXJEAkWvgN0yMEIuPOM", "createdAt": "2025-06-04T05:26:19.247Z", "updatedAt": "2025-06-04T05:26:20.454Z", "properties": { "avatarUrl": "https://storage.googleapis.com/zootools-beta/workspaces/wrk_ENGTK86JEXXckyDVJthKkPer/contacts/ctc_E7g1zoXJEAkWvgN0yMEIuPOM/avatar", "birthday": null, "city": null, "countryCode": "US", "email": "test2@example.com", "firstName": "Test2", "language": "en-US", "lastName": "User2", "phone": null, "status": "SUBSCRIBED", "timezone": "America/New_York", "company": "ACME Corp" }, "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer" }}``` ### POST/v1/contacts Creates a new contact or updates an existing one if a contact with the same email already exists (upsert). This is the most common endpoint used by Zapier integrations to add contacts from other platforms like forms, CRMs, and e-commerce systems. **Important:** All contact data must be wrapped in a `properties` object. Request ```curl -X POST "https://api.smashsend.com/v1/contacts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "properties": { "email": "jane.smith@example.com", "firstName": "Jane", "lastName": "Smith", "phone": "+1987654321", "countryCode": "US", "language": "en-US", "timezone": "America/New_York", "company": "Zapier Inc", "jobTitle": "Developer", "leadSource": "zapier_integration" } }'``` Response ```{ "contact": { "id": "ctc_B7OSYsnTUVwBPOGrgYTjvSGS", "createdAt": "2025-06-04T05:25:54.134Z", "updatedAt": null, "properties": { "avatarUrl": null, "birthday": null, "city": null, "countryCode": null, "email": "test@example.com", "firstName": "Test", "language": null, "lastName": "User", "phone": null, "status": "SUBSCRIBED" }, "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer" }}``` ### DELETE/v1/contacts/by-email/{email} Permanently deletes a contact by email address. Use with caution as this action cannot be undone. Request ```curl -X DELETE "https://api.smashsend.com/v1/contacts/by-email/john.doe@example.com" \ -H "Authorization: Bearer YOUR_API_KEY"``` Response ```{ "contact": { "id": "ctc_abc123def456", "createdAt": "2024-03-20T12:00:00Z", "updatedAt": "2024-03-20T12:00:00Z", "properties": { "avatarUrl": null, "birthday": null, "city": null, "countryCode": "US", "email": "john.doe@example.com", "firstName": "John", "language": "en-US", "lastName": "Doe", "phone": null, "status": "SUBSCRIBED", "timezone": "America/New_York" }, "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer" }, "isDeleted": true}``` ### DELETE/v1/contacts/{contactId} Permanently deletes a contact by ID. Use with caution as this action cannot be undone. Request ```curl -X DELETE "https://api.smashsend.com/v1/contacts/ctc_abc123def456" \ -H "Authorization: Bearer YOUR_API_KEY"``` Response ```{ "contact": { "id": "ctc_abc123def456", "createdAt": "2024-03-20T12:00:00Z", "updatedAt": "2024-03-20T12:00:00Z", "properties": { "avatarUrl": null, "birthday": null, "city": null, "countryCode": "US", "email": "john.doe@example.com", "firstName": "John", "language": "en-US", "lastName": "Doe", "phone": null, "status": "SUBSCRIBED", "timezone": "America/New_York" }, "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer" }, "isDeleted": true}``` ### GET/v1/contacts/search Search for contacts by email address. Perfect for Zapier “Find Contact” actions before creating or updating. Returns `null` if no contact is found with the given email. Request ```curl "https://api.smashsend.com/v1/contacts/search?email=john.doe@example.com" \ -H "Authorization: Bearer YOUR_API_KEY"``` Response (Contact Found) ```{ "contact": { "id": "ctc_abc123def456", "createdAt": "2024-03-20T12:00:00Z", "updatedAt": "2024-03-20T12:00:00Z", "properties": { "avatarUrl": null, "birthday": null, "city": null, "countryCode": "US", "email": "john.doe@example.com", "firstName": "John", "language": "en-US", "lastName": "Doe", "phone": null, "status": "SUBSCRIBED", "timezone": "America/New_York", "company": "ACME Corp" }, "workspaceId": "wrk_ENGTK86JEXXckyDVJthKkPer" }}``` Response (Contact Not Found) ```{ "contact": null}``` ## Error Handling The API uses standard HTTP status codes. Common error responses: 400 - Bad Request ```{ "statusCode": 400, "error": "Bad Request", "message": "Invalid email format"}``` 404 - Not Found ```{ "statusCode": 404, "error": "Not Found", "message": "Contact not found"}``` 409 - Conflict ```{ "statusCode": 409, "error": "Conflict", "message": "Contact with this email already exists"}``` ## Custom Properties Custom properties allow you to store additional data with contacts. They're commonly used to sync data from CRMs, e-commerce platforms, and other business tools via Zapier. Before using custom properties in API calls, you need to create them in your SMASHSEND dashboard or via the Contact Properties API. See the **Contact Properties API** documentation for more details. Common Custom Property Examples ```{ "properties": { "email": "contact@example.com", "firstName": "John", "lastName": "Doe", "company": "ACME Corp", "jobTitle": "Marketing Manager", "leadSource": "website", "industry": "Technology", "annualRevenue": "100000", "isCustomer": true, "lastPurchaseDate": "2024-03-15" }}``` CRM Integration Properties ```{ "properties": { "email": "contact@example.com", "firstName": "John", "lastName": "Doe", "company": "ACME Corp", "jobTitle": "Marketing Manager", "industry": "Technology", "annualRevenue": 250000, "leadScore": 85, "lastContactDate": "2024-03-15", "isQualified": true, "salesRep": "John Smith", "dealStage": "Proposal" }}``` E-commerce Integration Properties ```{ "properties": { "email": "customer@example.com", "firstName": "Jane", "lastName": "Smith", "customerSince": "2023-06-15", "totalOrders": 12, "lifetimeValue": 1850.00, "preferredCategory": "Electronics", "isVip": true, "lastPurchaseDate": "2024-03-10", "loyaltyTier": "Gold", "averageOrderValue": 154.17 }}``` Form Integration Properties ```{ "properties": { "email": "lead@example.com", "firstName": "Mike", "lastName": "Johnson", "formSource": "Contact Us Page", "referralSource": "Google Ads", "interests": "Email marketing, automation, analytics", "budget": "$1000-5000", "timeline": "Next 3 months", "hasOptedInMarketing": true, "companySize": "50-200 employees", "currentTool": "Mailchimp" }}``` Multi-Select Properties (Tags) For multi-select properties (like tags or categories), you can send an array of values to completely replace the contact's current values. This makes syncing from external systems straightforward - just send the desired final state. ```{ "properties": { "email": "contact@example.com", "tags": ["customer", "enterprise", "priority"], "interests": ["email-marketing", "automation"], "categories": ["premium"] }}``` **How it works:** When you send an array for a multi-select property, SMASHSEND will replace all existing values with the new ones. Examples: ```// Contact currently has tags: ["customer", "trial"]// You send: { "tags": ["customer", "enterprise"] }// Result: Contact will have tags: ["customer", "enterprise"]// ("trial" is removed, "enterprise" is added) // Contact currently has tags: ["customer", "trial"] // You send: { "tags": [] }// Result: Contact will have no tags (all removed) // Contact currently has tags: ["customer"]// You send: { "tags": ["customer"] }// Result: No change (same values)``` Advanced: Granular Control (Add/Remove) For advanced use cases where you need precise control over what gets added or removed without fetching current values, use the structured format with `add` and `remove` arrays: ```{ "properties": { "email": "contact@example.com", "tags": { "add": ["vip", "enterprise"], "remove": ["trial", "free"] }, "interests": { "add": ["api-access"], "remove": [] } }}``` This is useful for webhooks or event-driven updates where you only know what changed, not the complete desired state. ## Zapier Integration Tips 1\. Use “Search then Create” Pattern Always search for existing contacts by email before creating new ones to avoid duplicates. 2\. Leverage Custom Properties Use custom properties to sync additional data from your source systems like CRM fields, e-commerce data, or form responses. 3\. Handle Rate Limits For bulk operations, consider using batch endpoints or implementing delays between requests. 4\. Use Webhooks for Real-time Updates Instead of polling for changes, set up webhooks to get notified when contacts are created, updated, or deleted. ## Working with Timezones SMASHSEND supports timezone-aware email delivery to maximize engagement by sending emails at optimal times in each contact's local timezone. The `timezone` field accepts IANA timezone identifiers and enables smart delivery features. Timezone Field **Type:** String (optional) **Format:** IANA timezone identifier (e.g., `America/New_York`) **Validation:** Must be a valid IANA timezone or null **Default:** `null` (will be auto-inferred from `countryCode` if available) Supported Timezone Values SMASHSEND supports all **597 IANA timezone identifiers**. Common examples: North America: ```America/New_York # Eastern Time (UTC-05:00/-04:00)America/Los_Angeles # Pacific Time (UTC-08:00/-07:00)America/Chicago # Central Time (UTC-06:00/-05:00)America/Denver # Mountain Time (UTC-07:00/-06:00)America/Toronto # Eastern Time CanadaAmerica/Vancouver # Pacific Time Canada``` Europe: ```Europe/London # Greenwich Mean Time (UTC+00:00/+01:00)Europe/Paris # Central European Time (UTC+01:00/+02:00)Europe/Berlin # Central European Time (UTC+01:00/+02:00)Europe/Rome # Central European Time (UTC+01:00/+02:00)Europe/Madrid # Central European Time (UTC+01:00/+02:00)Europe/Amsterdam # Central European Time (UTC+01:00/+02:00)``` Asia Pacific: ```Asia/Tokyo # Japan Standard Time (UTC+09:00)Asia/Shanghai # China Standard Time (UTC+08:00)Asia/Singapore # Singapore Standard Time (UTC+08:00)Asia/Seoul # Korea Standard Time (UTC+09:00)Asia/Kolkata # India Standard Time (UTC+05:30)Asia/Dubai # Gulf Standard Time (UTC+04:00)Australia/Sydney # Australian Eastern Time (UTC+10:00/+11:00)Australia/Melbourne # Australian Eastern Time (UTC+10:00/+11:00)Pacific/Auckland # New Zealand Time (UTC+12:00/+13:00)``` Getting Timezone Data from Libraries ```// Install: npm install moment-timezoneimport moment from 'moment-timezone'; // Get all timezone names (597 total)const allTimezones = moment.tz.names();console.log(allTimezones); // ["Africa/Abidjan", "Africa/Accra", ...] // Get popular timezones for dropdownsconst popularTimezones = [ 'America/New_York', 'America/Los_Angeles', 'Europe/London', 'Europe/Paris', 'Asia/Tokyo', 'Australia/Sydney']; // Validate timezoneconst isValid = !!moment.tz.zone('America/New_York'); // trueconst isInvalid = !!moment.tz.zone('Invalid/Zone'); // false // Get current offset for timezoneconst offset = moment().tz('America/New_York').format('Z'); // "-04:00" // Browser detection (no library needed)const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;console.log(userTimezone); // e.g., "America/New_York"``` Usage Examples Create contact with timezone: ```{ "properties": { "email": "user@example.com", "firstName": "John", "lastName": "Doe", "timezone": "America/New_York", "countryCode": "US", "language": "en-US" }}``` Update contact timezone (using POST with existing email): ```{ "properties": { "email": "user@example.com", "timezone": "Europe/London" }}``` Validation & Error Handling Invalid timezone values return a `400 Bad Request` error: ```{ "statusCode": 400, "error": "Bad Request", "message": "Invalid/Timezone is not a valid IANA timezone identifier"}``` **Valid Examples:** ✅ `“America/New_York”` ✅ `“Europe/London”` ✅ `“Asia/Tokyo”` ✅ `null` (will be auto-inferred) **Invalid Examples:** ❌ `“EST”` (use `America/New_York` instead) ❌ `“GMT+5”` (use `Asia/Kolkata` instead) ❌ `“Invalid/Zone”` Auto-Detection & Smart Features **Automatic Timezone Inference (Best Effort):** When `timezone` is not provided but `countryCode` is available, SMASHSEND automatically infers the most likely timezone as a **best effort**. However, this may not be accurate for countries with multiple timezones. **⚠️ Important:** For optimal email delivery, always provide the explicit `timezone` when possible. Inference is a fallback feature and may not be accurate for all users. ```{ "properties": { "email": "user@example.com", "countryCode": "US" // Will infer "America/New_York" (Eastern Time) // But user might be in Pacific Time! }} // Better approach - detect timezone explicitly:{ "properties": { "email": "user@example.com", "countryCode": "US", "timezone": "America/Los_Angeles" // Explicit timezone is more accurate }}``` **Inference Limitations:** • **Multi-timezone countries**: US has 6+ timezones, Russia has 11 • **Default bias**: We default to the most populous timezone • **Border regions**: Users near timezone boundaries may be inaccurate • **Travelers**: Current location may differ from home timezone **Best Practice:** Use browser detection or user input to get the actual timezone rather than relying on country-based inference. **Smart Email Delivery:** • Emails are sent during optimal hours (7 AM - 6 PM) in the contact's timezone • Automatic DST (Daylight Saving Time) handling • Improved open rates through timezone-aware scheduling • Better user experience with localized send times ## Working with Country Codes The `countryCode` field stores the contact's country using ISO 3166-1 alpha-2 country codes. Country Code Field **Type:** String (optional) **Format:** Two-letter ISO 3166-1 alpha-2 code (e.g., `US`, `GB`, `CA`) **Validation:** Must be a valid ISO country code **Case:** Uppercase (automatically normalized) Common Country Codes ```US # United StatesCA # CanadaGB # United KingdomDE # GermanyFR # FranceAU # AustraliaJP # JapanBR # BrazilIN # IndiaMX # Mexico``` Getting Country Data from Browser ```// Get user's country from localeconst userLocale = navigator.language || navigator.userLanguage;const countryCode = userLocale.split('-')[1]; // e.g., "en-US" → "US" // Using Geolocation API (requires user permission)if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(async (position) => { // Use a geocoding service to get country from coordinates const response = await fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${position.coords.latitude}&longitude=${position.coords.longitude}`); const data = await response.json(); console.log(data.countryCode); // e.g., "US" });}``` ## Working with Language Codes The `language` field stores the contact's preferred language using ISO 639-1 language codes. Language Field **Type:** String (optional) **Format:** ISO 639-1 two-letter code (e.g., `en`, `es`, `fr`) or locale format (e.g., `en-US`, `pt-BR`) **Validation:** Must be a valid ISO language code **Case:** Lowercase (automatically normalized) Common Language Codes ```en # Englishes # Spanishfr # Frenchde # Germanit # Italianpt # Portugueseja # Japaneseko # Koreanzh # Chinesear # Arabicru # Russianhi # Hindi``` Locale Format (with country): ```en-US # English (United States)en-GB # English (United Kingdom)es-ES # Spanish (Spain)es-MX # Spanish (Mexico)pt-BR # Portuguese (Brazil)zh-CN # Chinese (Simplified)``` Getting Language Data from Browser ```// Get user's preferred languageconst userLanguage = navigator.language || navigator.userLanguage;console.log(userLanguage); // e.g., "en-US", "es-MX", "fr-FR" // Extract just the language codeconst languageCode = userLanguage.split('-')[0]; // e.g., "en-US" → "en" // Get all user languages (in order of preference)const userLanguages = navigator.languages;console.log(userLanguages); // e.g., ["en-US", "en", "es"]``` ## Complete Integration Example Here's a complete example showing how to detect and send all geographic/locale data from a web form using **explicit detection** (recommended): ```// RECOMMENDED: Detect user's locale information explicitlyasync function createContactWithLocaleData(email, firstName, lastName) { // ✅ BEST: Detect timezone explicitly from browser const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; // ✅ BEST: Detect language from browser preferences const language = navigator.language || navigator.userLanguage; // ✅ BETTER: Extract country from language if available const countryCode = language.includes('-') ? language.split('-')[1] : null; // Create contact with explicit data (most accurate) const response = await fetch('/v1/contacts', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ properties: { email: email, firstName: firstName, lastName: lastName, timezone: timezone, // ✅ Explicit timezone (most accurate) language: language, // ✅ User's actual language preference countryCode: countryCode, // ✅ Detected from locale detectionMethod: "browser" // Custom property for tracking } }) }); return response.json();} // FALLBACK: If you only have country dataasync function createContactWithCountryOnly(email, firstName, lastName, countryCode) { const response = await fetch('/v1/contacts', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ properties: { email: email, firstName: firstName, lastName: lastName, countryCode: countryCode, // ⚠️ SMASHSEND will infer timezone (best effort) detectionMethod: "country_inference" } }) }); return response.json();} // Usage examplescreateContactWithLocaleData('user@example.com', 'John', 'Doe'); // ✅ RecommendedcreateContactWithCountryOnly('user@example.com', 'Jane', 'Smith', 'US'); // ⚠️ Fallback``` Accuracy Recommendations **1\. Always Use Explicit Timezone Detection:** Use `Intl.DateTimeFormat().resolvedOptions().timeZone` in browsers or equivalent methods in your platform to get the user's actual timezone. **2\. Validate Before Sending:** Use timezone libraries to validate timezone identifiers before sending them to SMASHSEND to avoid validation errors. **3\. Handle Multi-Timezone Countries:** Countries like the US, Russia, Canada, and Australia have multiple timezones. Always ask users or detect their specific timezone rather than assuming from country alone. Benefits of Complete Locale Data **Enhanced Personalization:** • Send emails in the contact's preferred language • Respect cultural preferences and holidays • Optimize send times for local timezones • Improve deliverability with localized content **Better Analytics:** • Segment audiences by geography and language • Track engagement by timezone and country • Optimize campaigns for different regions • Understand global customer distribution **Compliance & UX:** • Respect time-based communication preferences • Comply with regional regulations (GDPR, CAN-SPAM) • Provide better user experience with localized timing • Reduce unsubscribes through timezone awareness Recommended Libraries by Language **JavaScript/TypeScript:** • **moment-timezone**: Full timezone support with DST handling • **date-fns-tz**: Lightweight timezone utilities • **luxon**: Modern date/time library with timezone support • **Intl.DateTimeFormat**: Built-in browser API **Python:** • **pytz**: Comprehensive timezone library • **zoneinfo**: Built-in timezone support (Python 3.9+) • **pendulum**: Modern date/time library **Other Languages:** • **Ruby**: tzinfo gem or built-in Time class • **PHP**: Carbon library or built-in DateTime • **Java**: java.time package (built-in) • **C#**: NodaTime library or TimeZoneInfo • **Go**: time package (built-in) + go-timezone • **Rust**: chrono-tz crate Country & Language Detection Libraries **JavaScript:** • **country-list**: ISO country codes and names • **i18n-iso-countries**: Country code utilities • **iso-639-1**: Language code utilities • **Intl.Locale**: Built-in locale handling **GeoIP APIs for Country Detection:** • **MaxMind GeoIP2**: Professional IP geolocation • **IPinfo.io**: IP geolocation with free tier • **BigDataCloud**: Free IP geolocation API • **ipapi.co**: Simple IP geolocation • **Browser Geolocation API**: GPS-based (requires permission)
