Klaviyo

Within the Nutrition Warehouse's dedicated page for Klaviyo, you'll find essential information and code snippets meticulously curated to optimize email marketing strategies. These resources empower you to harness the full potential of Klaviyo's capabilities, driving customer engagement and business growth. Explore, implement, and elevate your nutrition-focused marketing campaigns with precision.


RUN LOCALLY

Cleaning Email and Mobile numbers

This is used to clean a RAW CSV with Email and Mobile numbers. Ensure both columns exists within the CSV and named as Email and Mobile/Cell.

Cleaning Email and Mobile numbers

import pandas as pd
import re

# 1. Load the Data
# Replace 'your_file.csv' with the actual path of your file.
try:
    df = pd.read_csv('your_file.csv', dtype={'Mobile/Cell': str, 'Email': str})
except FileNotFoundError:
    print("Error: csv not found. Please check the file path and try again.")
    exit()

# Replace any potential NaN (empty) values with empty strings for consistency.
df['Mobile/Cell'] = df['Mobile/Cell'].fillna('')
df['Email'] = df['Email'].fillna('')

# Helper Functions
def is_valid_au_mobile(number):
    """Checks if a number is a valid AU mobile format (04, 4, or +614)."""
    if not number:
        return False
    return re.match(r'^(04\d{8}|4\d{8}|\+614\d{8})$', number) is not None

def format_to_plus61(number):
    """Formats a valid number to the +61 standard."""
    if number.startswith('04'):
        return '+61' + number[1:]
    if number.startswith('4'):
        return '+61' + number
    return number

def is_valid_email(email):
    """Checks if an email string has a valid format."""
    if not email:
        return False
    regex = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
    return re.match(regex, email) is not None

# 2. Clean and Format Mobile Numbers
# First, count the numbers that are present but invalid.
invalid_phone_mask = (df['Mobile/Cell'] != '') & (~df['Mobile/Cell'].apply(is_valid_au_mobile))
num_invalid_phones = invalid_phone_mask.sum()

# Now, apply the cleaning and formatting.
def clean_and_format_phone(number):
    if is_valid_au_mobile(number):
        return format_to_plus61(number)
    return '' # Return empty for invalid numbers

df['Mobile/Cell'] = df['Mobile/Cell'].apply(clean_and_format_phone)

#3. Deduplicate Mobile/Cell Column
mobiles = df.loc[df['Mobile/Cell'] != '', 'Mobile/Cell']
duplicate_indices_mobiles = mobiles[mobiles.duplicated(keep='first')].index
num_mobile_duplicates = len(duplicate_indices_mobiles)
df.loc[duplicate_indices_mobiles, 'Mobile/Cell'] = ''

#4. Deduplicate Email Column
emails = df.loc[df['Email'] != '', 'Email']
duplicate_indices_emails = emails[emails.duplicated(keep='first')].index
num_email_duplicates = len(duplicate_indices_emails)
df.loc[duplicate_indices_emails, 'Email'] = ''

#5. Validate Email Format
# Before clearing, identify emails that are invalid and not already empty.
invalid_email_mask = (df['Email'] != '') & (~df['Email'].apply(is_valid_email))
num_invalid_emails = invalid_email_mask.sum()

# Apply the validation function to clear invalid emails.
df['Email'] = df['Email'].apply(lambda email: email if is_valid_email(email) else '')

# Save the fully cleaned DataFrame to a new CSV file. Insert path to where CSV is to be saved.
df.to_csv('contacts_master_cleaned.csv', index=False)

print("Processing Complete!")
print("---")
print(f"Cleared {num_invalid_phones} invalid mobile numbers.")
print(f"Cleared {num_invalid_emails} invalid formatted emails.")
print(f"Removed {num_mobile_duplicates} duplicate mobile numbers.")
print(f"Removed {num_email_duplicates} duplicate emails.")
print("---")
print("Final data saved to 'contacts_master_cleaned.csv'")

Cleaning

Email validity is checked using regex and duplicates are removed

Mobile number validity checked with Australia starting formating 04, 4 or 614 and duplicated are removed Invalid and duplicates values will return Null and keep remaining data in the row unaffected File read and export - Full file path required


Cleaning Email and Mobile numbers

Add copy here

Webhook payload

<div>

Cleaning Email and Mobile numbers

Add copy here

Webhook payload

<div>

Webhook subscribing emails ids from Rex

This JSON webhook payload initiates a bulk subscription job in Klaviyo, adding subscribers to the specified list with the source labeled as "Store sign up" for marketing SMS messages, using the provided phone number and Klaviyo ID.

Webhook payload

{
  "data": {
    "type": "profile-subscription-bulk-create-job",
    "attributes": {
      "list_id": "## REPLACE_WITH_ID ##",
      "custom_source": "Store sign up",
      "subscriptions": [
        {
          "channels": {
            "sms": [
              "MARKETING"
            ]
          },
          "phone_number": "{{ person.phone_number }}",
          "profile_id": "{{ person.KlaviyoID }}"
        }
      ]
    }
  }
}

Replace the list_id with the corresponding id served from Klayivo


SMS Sub webhook in Napkin

This SMS subscription webhook in Napkin allows you to seamlessly integrate SMS marketing by capturing email addresses from your store sign-ups and automatically sending them to Klaviyo

Sub webhook

{
  "email": "{{ person.email }}",
  "list_id": "## REPLACE_WITH_ID ##",
  "source": "Store signup"
}

Replace the list_id with the corresponding id served from Klayivo


Napkin Payload

This JavaScript code snippet defines a webhook for bulk email subscription requests to Klaviyo, utilizing Axios to send data to Klaviyo's API based on incoming Napkin requests.

Napkin Payload

const axios = require('axios');
/**
* @param {NapkinRequest} req
* @param {NapkinResponse} res
*/
export default async (req, res) => {
  const url = 'https://a.klaviyo.com/api/profile-subscription-bulk-create-jobs/';

  const data = {
    data: {
      type: 'profile-subscription-bulk-create-job',
      attributes: {
        list_id: req.body.list_id,
        custom_source: req.body.source,
        subscriptions: [{email: req.body.email}]
      }
    }
  }

  const headers = {
    headers:{
      "accept": 'application/json',
      "revision": '2023-02-22',
      "content-type": 'application/json',
      "Authorization": process.env.Authorization
    }
  }

  try {
    const response = await axios.post(url, data, headers);
    console.log(response.data);
  } catch (error) {
    console.error(error.message);
  }
}

Replace the list_id with the corresponding id served from Klayivo