Profile Objects in Salesforce Marketing Cloud Personalization allow you to store structured, custom data about your users—beyond basic attributes like name or email. This enables powerful cross-domain personalization, targeted segmentation, and contextual messaging.
Let's dive into a real-world example that shows exactly how to set up and use Profile Objects effectively.
Business Context: You operate a vehicle website that allows users to book after-sales service appointments across multiple domains (booking.yoursite.com, info.yoursite.com, finance.yoursite.com).
Goal: Store each appointment's details within the user profile for:
In Salesforce Marketing Cloud Personalization:
Object Name: ServiceAppointment
ServiceAppointment
Key Attributes to Configure:
appointmentId
serviceType
vehicleModel
appointmentDate
dealerName
status
Pro Tip: Think about what data you'll actually use for personalization or reporting. Don't create attributes you won't leverage—keep it action-driven.
When a user completes a service booking form and clicks "Confirm Appointment," capture the data with this client-side code:
Copy// Service Appointment Form Submission document.querySelector('#appointment-form').addEventListener('submit', function(e) { e.preventDefault(); // Get form data const formData = new FormData(this); const appointmentId = 'APT-' + Date.now(); // Generate unique ID // Send to Salesforce MCP SalesforceInteractions.sendEvent({ action: "Book Service Appointment", user: { profileObjects: { ServiceAppointment: [ { id: appointmentId, attributes: { serviceType: formData.get('serviceType'), vehicleModel: formData.get('vehicleModel'), appointmentDate: formData.get('appointmentDate'), dealerName: formData.get('dealerName'), status: "scheduled" } } ] } } }); // Show confirmation alert('Appointment booked successfully! ID: ' + appointmentId); // Submit form to backend this.submit(); });
// Service Appointment Form Submission document.querySelector('#appointment-form').addEventListener('submit', function(e) { e.preventDefault(); // Get form data const formData = new FormData(this); const appointmentId = 'APT-' + Date.now(); // Generate unique ID // Send to Salesforce MCP SalesforceInteractions.sendEvent({ action: "Book Service Appointment", user: { profileObjects: { ServiceAppointment: [ { id: appointmentId, attributes: { serviceType: formData.get('serviceType'), vehicleModel: formData.get('vehicleModel'), appointmentDate: formData.get('appointmentDate'), dealerName: formData.get('dealerName'), status: "scheduled" } } ] } } }); // Show confirmation alert('Appointment booked successfully! ID: ' + appointmentId); // Submit form to backend this.submit(); });
Copy// Update appointment status (e.g., when user completes service) function updateAppointmentStatus(appointmentId, newStatus) { SalesforceInteractions.sendEvent({ action: "Update Appointment Status", user: { profileObjects: { ServiceAppointment: [ { id: appointmentId, attributes: { status: newStatus, // "completed" or "canceled" completedDate: newStatus === "completed" ? new Date().toISOString() : null } } ] } } }); } // Usage updateAppointmentStatus('APT-1234567890', 'completed');
// Update appointment status (e.g., when user completes service) function updateAppointmentStatus(appointmentId, newStatus) { SalesforceInteractions.sendEvent({ action: "Update Appointment Status", user: { profileObjects: { ServiceAppointment: [ { id: appointmentId, attributes: { status: newStatus, // "completed" or "canceled" completedDate: newStatus === "completed" ? new Date().toISOString() : null } } ] } } }); } // Usage updateAppointmentStatus('APT-1234567890', 'completed');
Best Practice: Always generate a unique id for each Profile Object entry. This allows you to update specific records later without creating duplicates.
id
Now that appointment data is stored in user profiles, here's how to leverage it:
Create audience segments like:
Recognize users across domains:
Send timely reminders:
Target based on service history:
Copy// In your sitemap or campaign logic const upcomingAppointments = user.profileObjects.ServiceAppointment.filter(apt => { const daysUntil = (new Date(apt.appointmentDate) - new Date()) / (1000 * 60 * 60 * 24); return apt.status === "scheduled" && daysUntil >= 0 && daysUntil <= 3; }); if (upcomingAppointments.length > 0) { const appointment = upcomingAppointments[0]; // Show personalized banner return { contentZones: [ { name: "hero-banner", content: ` Upcoming Service Appointment Vehicle: ${appointment.vehicleModel} Service: ${appointment.serviceType} Date: ${formatDate(appointment.appointmentDate)} Dealer: ${appointment.dealerName} View Details ` } ] }; }
// In your sitemap or campaign logic const upcomingAppointments = user.profileObjects.ServiceAppointment.filter(apt => { const daysUntil = (new Date(apt.appointmentDate) - new Date()) / (1000 * 60 * 60 * 24); return apt.status === "scheduled" && daysUntil >= 0 && daysUntil <= 3; }); if (upcomingAppointments.length > 0) { const appointment = upcomingAppointments[0]; // Show personalized banner return { contentZones: [ { name: "hero-banner", content: ` Upcoming Service Appointment Vehicle: ${appointment.vehicleModel} Service: ${appointment.serviceType} Date: ${formatDate(appointment.appointmentDate)} Dealer: ${appointment.dealerName} View Details ` } ] }; }
Vehicle: ${appointment.vehicleModel}
Service: ${appointment.serviceType}
Date: ${formatDate(appointment.appointmentDate)}
Dealer: ${appointment.dealerName}
Copy// Einstein Recipe for email personalization export class AppointmentReminderRecipe implements CampaignTemplateComponent { run(context: CampaignComponentContext) { const upcomingAppointments = context.user.profileObjects.ServiceAppointment .filter(apt => { const daysUntil = this.getDaysUntil(apt.appointmentDate); return apt.status === "scheduled" && daysUntil === 3; }); if (upcomingAppointments.length > 0) { return { // Send reminder email 3 days before emailTemplate: "appointment-reminder", emailData: { appointmentDetails: upcomingAppointments[0], userName: context.user.attributes.firstName } }; } return null; } getDaysUntil(dateString: string): number { return Math.ceil( (new Date(dateString).getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24) ); } }
// Einstein Recipe for email personalization export class AppointmentReminderRecipe implements CampaignTemplateComponent { run(context: CampaignComponentContext) { const upcomingAppointments = context.user.profileObjects.ServiceAppointment .filter(apt => { const daysUntil = this.getDaysUntil(apt.appointmentDate); return apt.status === "scheduled" && daysUntil === 3; }); if (upcomingAppointments.length > 0) { return { // Send reminder email 3 days before emailTemplate: "appointment-reminder", emailData: { appointmentDetails: upcomingAppointments[0], userName: context.user.attributes.firstName } }; } return null; } getDaysUntil(dateString: string): number { return Math.ceil( (new Date(dateString).getTime() - new Date().getTime()) / (1000 * 60 * 60 * 24) ); } }
Data Management: Profile Objects support arrays, so each user can have multiple appointments stored. The system automatically manages data structure and retrieval.
Copy// 1. Add new Profile Object entry SalesforceInteractions.sendEvent({ user: { profileObjects: { ObjectName: [{ id: "unique-id", attributes: { /* your data */ } }] } } }); // 2. Update existing entry (same id) SalesforceInteractions.sendEvent({ user: { profileObjects: { ObjectName: [{ id: "existing-id", attributes: { status: "updated" } }] } } }); // 3. Access in campaigns/recipes const userObjects = user.profileObjects.ObjectName; const filteredObjects = userObjects.filter(obj => obj.status === "active"); // 4. Use in segmentation // In Audience Builder: "User Profile Object → ServiceAppointment → status equals 'scheduled'"
// 1. Add new Profile Object entry SalesforceInteractions.sendEvent({ user: { profileObjects: { ObjectName: [{ id: "unique-id", attributes: { /* your data */ } }] } } }); // 2. Update existing entry (same id) SalesforceInteractions.sendEvent({ user: { profileObjects: { ObjectName: [{ id: "existing-id", attributes: { status: "updated" } }] } } }); // 3. Access in campaigns/recipes const userObjects = user.profileObjects.ObjectName; const filteredObjects = userObjects.filter(obj => obj.status === "active"); // 4. Use in segmentation // In Audience Builder: "User Profile Object → ServiceAppointment → status equals 'scheduled'"
Profile Objects transform Salesforce Marketing Cloud Personalization from basic attribute tracking to rich, structured data management. By following this practical approach:
Remember: Keep it simple, action-driven, and focused on data you'll actually use. Start with one Profile Object, validate it works, then expand based on your personalization needs.