Pages

Saturday, September 13, 2025

B2B Marketing Analytics: Account Engagement Email Datasets

To continue from the previous blog, if you get an error when creating the B2B Marketing Analytics app, once the app is created successfully, you will see a new app for the B2B Marketing Analytics app; the app name is based on what you entered when creating the app. 

Within the app, you will get many datasets available, starting with "Account Engagement". The datasets are updated daily by Salesforce from MCAE to CRM Analytics. You can monitor it from Jobs Monitor, look for jobs that start with "pd".



You will see the dataset in the B2B Marketing Analytics app, including when the data is refreshed.  

** To get the Prospect and Activity dataset, you need to enable "Get Prospect and Activity Data" in the B2B Marketing Analytics setup menu, which is under Optional Features for B2B Marketing Analytics, then select "Include Prospect and Activity dataset?" when creating the app.



The app comes with multiple dashboards, such as "Engagement," which shows MCAE data, such as:

  • List Email Engagement
  • Email Template Engagement
  • Forms Engagement
  • Landing Page Engagement
These include different metrics for each type of engagement.

In this blog, I want to discuss two of the datasets: the Account Engagement Emails dataset and the Account Engagement Email templates dataset.


Account Engagement Emails dataset 

As per this article, B2B Marketing Analytics Datasets, the dataset API Name is pdEmail. Each row represents 1 day of statistics for an individual list email. You can see the field description in the article above. Let's see some samples:

For Email ID = 2012421957, emails are sent on 2025-09-10, so you see the same Send On Date applied to rows 1 and 2. Some recipients open and click on the same day, and a few open on 2025-09-11. The same applies for Email ID = 2011677609; emails are sent on 2025-09-09 for rows 3, 4, and 5; some people open and click on the same day and the following days. So, this is aggregate data per day per email.

Stats ID is the unique key in the dataset.

Available metrics for this dataset:

  • Click To Open Ratio
  • Delivery Rate
  • Opt Out Rate
  • Spam Complaint Rate
  • Total Bounced
  • Total CTR
  • Total Clicks
  • Total Delivered
  • Total HTML Opens
  • Total Hard Bounced
  • Total Opt Outs
  • Total Queued
  • Total Sent
  • Total Soft Bounced
  • Total Spam Complaint
  • Unique CTR
  • Unique Clicks
  • Unique HTML Open Rate
  • Unique HTML Opens


Account Engagement Email Templates dataset 

From the same article above, the dataset API Name is pdEmailTemplate. Each row represents 1 day of statistics for an individual email template. Email metrics are based on the emails built on selected email templates. 

The email sent here are not from the list email, but automated email, such as the Autoresponder email in Completion Action or Engagement program emails. The email here are not sent at the same time, but only when the user submits a form or receives an auto email from the engagement program.

The Send on Date field is not present on this dataset. This is because templates are reusable email designs that are not tied to specific send events.


We can see the same pattern here: each row represents an Email ID for a day, the total emails sent in a day, email opens, and clicks. 

Similar to the Email dataset, Stats ID is the unique key in the dataset, and almost all metrics are also available in this dataset:
  • Delivery Rate
  • Opt Out Rate
  • Spam Complaint Rate
  • Total CTR
  • Total Clicks
  • Total Delivered
  • Total HTML Opens
  • Total Hard Bounced
  • Total Opt Outs
  • Total Queued
  • Total Sent
  • Total Soft Bounced
  • Total Spam Complaint
  • Unique CTR
  • Unique Clicks
  • Unique HTML Opens

Note: There is an issue with the "Unique HTML Opens" field in this dataset. Here is the KI and idea.



Reference:

Friday, September 12, 2025

CRM Analytics: Enhancing Dashboard Filter with Custom Range Value

Requirement: Create a filter in the dashboard that bucket sums the Amount of customers into categories, such as low (< $500), medium ($500 - $1000), and high (> $1000). The result should be updated dynamically as filtered when other widgets are selected. The range of the Sum of Amount should be fixed, not depend on the Sum of Amount values in the data.

Solution: As each user can select different filters, such as Region, Product, etc., we can't stamp the sum of Amount in the dataflow/recipe. Binding is here to help.

Step-by-step:

1. Create Custom Query


  • We need "All" as the 1st value. This is when the dashboard loads; it will load all data. Remember to select the list widget selection type as "Single selection (required)".
  • The max value for All and the high (last) row can be set as high as possible.


2. Add Table Widget

  • Change the table to Compare Table
  • Group By: Customer or Account name
  • Change Column to Sum of Amount
  • Add a Filter to the Sum of Amount column, use the medium range, which is between 500 and 1000


3. Add Binding to the Filter Range
  • Select the table widget added.
  • Click Advanced Editor >> Query tab
  • Look for "aggregateFilters" with values 500 and 1000
  • Change 500, 1000 to the following
"{{coalesce(cell(static1_1.selection, 0, \"low\"), cell(static1_1.result, 0, \"low\")).asObject()}}",
"{{coalesce(cell(static1_1.selection, 0, \"high\"), cell(static1_1.result, 0, \"high\")).asObject()}}"
  • Save the query and done :) 


Result

See the table widget

no filter

6 months ago to today

6 months ago to today, and the Sum of Amount between $500 - $1000

Product X3 and X4, and the Sum of Amount between $500 - $1000







 




Tuesday, August 26, 2025

Salesforce: Shortcut create Task

Instead of starting with a blank task record when creating a task, we can prepopulate some fields when creating a specific task, and we can create multiple specific tasks.


As shown in the above screenshot, if clicking the New Task green icon opens a new form with blank fields, of course, if the button is in Account, the Related To will be auto-populated with the Account Name.

If we have a specific task where additional fields, such as Type, Subject, etc., should be the same, we can create a Custom Action for the Task. We can even hide the fields from the layout so that users cannot change the pre-populated fields manually when creating a task. The specific tasks will appear under the arrow next to the New Task button in a record page, such as Important-1 and Important-2 in the above screenshot.

Here are the steps to create the task and add it to page layout:


1. Create New Action under Global Actions


Open the action created to set predefined field values, and click Edit Layout to add/remove only relevant fields.



2. Add the action to the object page layout, such as for the Account Page layout

In this sample, I add both Important-1 and Important-2 to the Account Layout (under Salesforce Mobile and Lightning Experience Actions), while only adding Important-2 to the Account (Sales) Layout. 



In other scenarios, if you need the action to be always visible in Salesforce, you can add it to Publisher Layout (under Salesforce Mobile and Lightning Experience Actions), you can create multiple Publisher Layouts, and assign them by profile.


The same approach can be applied to the New Event as well.









Friday, August 15, 2025

CRM Analytics: Join or Augment Master to Child

When you augment the master table with a child table and there are multiple rows for the same master value, which row will the dataflow and recipe select?

Sample Data:


Master

Child

Dataflow

With "Look Up Multiple Values"

With "Look Up Single Values"

Summary:
  • Look Up Multiple Values: sum value of all numeric (measure) fields, while dimension (text) fields will retrieve all values, although only 1 value is shown when you browse the data in a lens. Check out this blog
  • Look Up Single Values: the first matching record for both numeric and text fields.

Recipe

With "Look Up Multiple Values"


Without "Look Up Multiple Values"

Summary:
  • With Look Up Multiple Values: sum value of all for numeric (measure) fields, while dimension (text) fields will retrieve all values, "all values" will be shown in the recipe, but in Lens, it will show 1 value only. 
  • Without Look Up Single Values: as per this article, Lookup - the lookup returns only the first matching record. However, in my test results, the system will get the row with the lowest value of the numeric field in sort order. If no numeric field, sort the text field alphabetically in ascending order and get the first row.



Reference:




Thursday, August 7, 2025

CRM Analytics: Sort Date field

I recently encountered working with a CSV file containing a date field in dd-MM-yyyy format. CRMA supports this format when manually loading the CSV file. Additionally, you can vote for this idea for more format support.


However, once the field is uploaded to CRMA, even if it is selected as a Date type, it is stored as dimensions, although it automatically generates multiple derived fields (like day, month, year, quarter, day_epoch, etc.).

This causes an issue when you add the field in a table widget and sort it. Because the generated date fields are dimensions, sorting is performed alphanumerically rather than in date order.



Here are a few workarounds:

1. Add the day_epoch field and sort with that field, although it is not a good user experience


2.  Load the CSV file in yyyy-MM-dd format


The system supports using the yyy-MM-dd format when uploading the CSV file



3. Use toDate() function in the dashboard, but this approach will stop your ability to edit the dashboard with clicks

q = load "ddmmyyyy";
q = foreach q generate q.'Id' as 'Id', q.'Amount' as 'Amount', q.'Date' as 'Date',toDate(Date_Year+"/"+Date_Month+"/"+Date_Day,"yyyy/MM/dd") as 'Date_Formatted';
q = order q by 'Date_Formatted' asc;
q = limit q 100;



4. Same as (3) by using toDate() function in a dataflow or recipe.

Check out the samples here and here.


Reference:



Thursday, July 31, 2025

MCAE: Open Prospect user interface with Prospect ID

If you have the Prospect ID, you can open the Prospect detail with the following URL with parameters:

With the Pardot user interface:
https://pi.pardot.com/prospect/read/id/179597888
https://pi.pardot.com/prospect/read?id=179597888

With the Account Engagement user interface:
https://mydomain.lightning.force.com/lightning/page/pardot/prospect?pardot__path=%2Fprospect%2Fread%2Fid%2F179597888




Monday, July 28, 2025

B2B Marketing Analytics: sfdc_internal__B2BMA

B2B Marketing Analytics, a CRM Analytics app within Salesforce, is designed to analyze marketing and sales data. It leverages datasets from Account Engagement (formerly Pardot) and Salesforce to provide insights into campaign performance, prospect behavior, and overall marketing effectiveness. 

You can create the B2B Marketing Analytics app from Analytics Studio, select B2B Marketing Analytics, and follow the wizard.



You need to enter your Pardot Account ID, then select optional features, such as Account-Based Marketing, Multi-Touch Attribution, Prospect and Activity dataset, etc. If you stopped with the following error:

Unable to create app based on template: [sfdc_internal__B2BMA].

* Your org does not currently meet minimum data requirements to proceed. Please fix the following issues before creating a 'B2B Marketing Analytics' Application:

In the 'sfdcDigest_Contact_CWA' node, the 'pi__grade__c' field doesn't exist, is deprecated, or isn't accessible to the Integration User. In the 'sfdcDigest_Contact_CWA' node, the 'pi__score__c' field doesn't exist, is deprecated, or isn't accessible to the Integration User.

You need to adjust the fields in both Lead and Contact, the Account Engagement Score (pi__score__c) and the Account Engagement Grade (pi__grade__c) fields, to make them visible (read-only is sufficient) to the Analytics Cloud Integration User profile. 

From the object manager, open each field and update the field-level security settings to grant visibility to the Analytics Cloud Integration User profile. 

Re-create the B2B Marketing Analytics app from Analytics Studio, and you should be able to pass the error.

Once the app is created, it can also be seen in the "Auto-Installed Apps" in the setup menu.





Monday, July 14, 2025

Salesforce Field history tracking on Record creation

In Salesforce, field history tracking can be setup for both standard and custom fields. 

On Record Creation
No, field history tracking does not log the initial values when a record is created.





On Record Update
Yes, it tracks changes to the specified fields when they are updated. It logs the old value, new value, user who made the change, and timestamp.


As noted in the last screenshot, the Mailing Country should ideally be auto-updated (from Flow, Trigger, etc.) after the record is created; however, the order can be misleading, as they have the same timestamp. If we use SOQL, will we see the difference? No, they may have the same exact milliseconds



Reference:

Monday, May 19, 2025

Salesforce: Sales Territories (I)

Sales Territory

As per the Summer '24 release, Enterprise Territory Management is now called Sales Territories

Out-of-the-box sharing mechanism to share record access to users based on the territory setup:
  • Account (View OR View and edit OR View, edit, transfer, and delete) 
  • Contact (No access OR View all OR View and edit all contacts associated with accounts)
  • Opportunity (No access OR View all OR View and edit all opportunities associated with accounts)
  • Case (No access OR View all OR View and edit all cases associated with accounts)
  • Leads (View, edit, and transfer OR View, edit, transfer, and delete)
* You can set access for the Contact and Lead if you use Private as the default internal access for contacts or cases.


Default access levels determine the default selection when creating new territory.


Territory Model

  • Defines the structure and assignment of sales territories and their associated accounts and users.
  • It's a hierarchical structure that organizes sales teams and their responsibilities.
  • Allow for flexible assignment of accounts and users to territories based on various criteria.
  • You can create and preview multiple models (2 for EE, 4 for UE/PE), but only one can be active at a time.
  • Once a model has been created, you can open it to Clone, Activate/Archive, or view more info, such as the activation date, etc.


Territory Type

  • It can help you group territories based on geography (e.g., North America, Europe), industry (e.g., Financial Services, Retail), or other factors.
  • To categorize territories at a more abstract level, not within the hierarchy itself.
  • Can be used in reports to analyze sales performance by type (e.g., compare sales across geographical regions). 
  • Every territory you create must have a territory type.
  • Territory types are used for organizing and creating territories only: they do not appear on territory model hierarchies.
  • Each territory type has a priority (it must be a number). For example, "1" indicates the highest or lowest priority.


Create New Territory

Once the Territory Type and Territory Model are created (Territory Model does not need to be activated), you can add the Territory from the "View Hierarchy" in a Territory Model. When creating a new territory, you need to select:
  • Territory Type 
  • Parent Territory, this is populated by default
  • Description (optional)
  • Access level for: Account, Contact, Opportunity, and Case


After a territory is created, you can:
  • Edit the Territory to set the Forecast Manager (for forecasting purposes)
  • Assign users
  • Manually add accounts
  • Create assignment rules
- An assignment rule can be assigned to multiple territories
  - You need to run the rule when the account or assignment criteria are changed
  • Create a child territory
- Inherited Assignment Rules from the parent territory
  • Click the 'View Accounts' button to see all Accounts that fit into the rule. 


Account

Add the Assigned Territories related list to the Account page layout, and you will see all Territories related to the Account





Reference:


Tuesday, May 13, 2025

Salesforce: Inactive User

When a Salesforce user has been deactivated, can we still assign the user as the record owner, or populate the user into a lookup field? 

1. Lookup Fields

From the Page Layout (UI): In the standard Salesforce user interface, you typically cannot select or populate a lookup field with an inactive user. The UI filters out inactive users from lookup fields to prevent confusion and ensure that only active users are selectable. Therefore, if you are trying to populate a lookup field from the page layout, you will not see inactive users listed.

From the Backend: You can populate lookup fields with inactive users through backend processes. This includes using data import tools, APIs, or automated processes (like Apex code, workflows, or triggers).


2. Record Ownership

From Setup > User Interface > User Interface, look for "Set Audit Fields upon Record Creation" and "Update Records with Inactive Owners" and enable it. Then, create a permission set with "Update Records with Inactive Owners" permission or enable the permission in the profile.

From the Page Layout (UI): In the standard Salesforce user interface, you typically cannot find or populate the record owner with an inactive user, even if you already have the above permission granted.

From the Backend: If you have "Update Records with Inactive Owners" permission enabled, you can populate record ownership with an inactive user through backend processes. This includes using data import tools, APIs, or automated processes (like Apex code, workflows, or triggers).

Without the permission enabled, you will receive the error message INACTIVE_OWNER_OR_USER:



Monday, May 12, 2025

CRM Analytics: Unsupported Salesforce Data

CRM Analytics (CRMA) is an analytics tool by Salesforce and is hosted in Salesforce's core platform; however, not all of Salesforce's objects and fields are supported by CRM Analytics data sync, including external objects, so you can't build a CRMA dashboard that uses those fields or objects. Check out the following article.

A supported object is one for which you can grant field-level access in Setup. If you're unable to assign field access to a Profile in Setup, that object is considered unsupported in CRMA, e.g. LoginHistory object.

The following statement from the above help article explains this: "To extract fields, the object must be visible to the Integration User. If you can’t manage a field’s visibility in Setup, you can’t grant the Integration User access to it."


Reference:



Friday, May 9, 2025

Salesforce: Retrieve Report Metadata (2)

In a previous blog, we discussed how to use package.xml in Workbench to retrieve report metadata. However, if we simply need to retrieve the report metadata, e.g., to understand the object of a field added to the report, we can simply use REST Explorer in Workbench.

Use GET method /services/data/v62.0/analytics/reports/00OGB00000Dt9Qv2AJ

Change the highlighted ID above to the report ID you want to analyze.


A few items to note from the above data:

  • factMap > T|T > aggregates - number of rows of the report
  • factMap > T|T > rows - the cell value of each row
  • reportMetadata > detailColumns - API field name for each column in the report, including the object name and whether it is a lookup field in the report type
  • reportMetadata > reportType - the report type used for the report

The same result (in XML format) you can get using REST Explore from Salesforce Inspector Reloaded.


Salesforce: Custom Report Type Metadata

The Spring '25 release enhanced the Custom Report Types user experience on the setup page; "Enhanced Custom Report Type Setup Page" in the "Reports and Dashboards Settings" should be enabled in the setup menu, which should be turned on by default; otherwise, your Salesforce admin needs to enable it.

The Custom Report Type is stored as metadata, which we shared here. How can I retrieve the Custom Report Type metadata without an IDE? You need to use a tool that supports REST Explorer:


1. Workbench

  • Go to Salesforce Workbench.
  • Log in with your Salesforce credentials.
  • Navigate to Utilities > REST Explorer.
  • Use the REST API endpoint mentioned above to retrieve report metadata.
Use the GET method, for example: 
/services/data/v62.0/analytics/report-types/Account_custom__c

You can get the report type API Name from the Custom Report Type user interface in the setup menu, and add a suffix of __c.



2. Salesforce Inspector Reloaded
/services/data/vXX.0/analytics/report-types/Account_custom__c





Tuesday, May 6, 2025

Salesforce: Upload Files to a Record

As shared in the Files sharing Query blog, a file can be shared with users, groups, records, and the content library. 

Let's see how the system works when uploading a file from a record related list.

By default, when a file is loaded from a record, the user who uploaded the file becomes the File Owner, and the record owner has extra permissions compared to other users; this applies when default file access is set to Set by Record, check this article.

File owner:


Record owner:

Users with edit access to the record <-- similar to the record owner

Users without edit access to the record:

Users with permission "Modify ALL records" or "Modify ALL data" <-- similar to file owner

Additionally, users with "Create Public Links" permission

The user will be able to share the file externally with an expiration date and a password:


Who Can Access?
Click the Share dropdown from the Files related list, and you can see "who can access"? Remember the  "Set by Record" mentioned earlier, and the ShareType in Query is "I", not "V".

Let's see it in a query:
SELECT Id, ShareType, ContentDocumentId, ContentDocument.Title, LinkedEntity.Name, LinkedEntity.Type FROM ContentDocumentLink WHERE ContentDocumentId = '069VZ000006928nYAA'


Share the file with another user as a Collaborator manually: 


Share the file with a Library
What happened when we shared the file with a library? 

Notice that the share type for the record and the user who shared earlier has changed to Viewer. When a file is added to a library, it means that access to the file follows access to the library; this setting can’t be changed. You can't manually re-share the file to the user as a Collaborator. You also can't remove the file from the Library. Vote for this idea. But, if you have a folder in a library, you can move the file within the library and the subfolder.



Reference:


Page-level ad