HubSpot Automation: Logging Who Changed What (And Why It Matters for E-commerce)
Hey there, ESHOPMAN readers! As your friendly HubSpot and e-commerce experts, we often talk about automating customer journeys and optimizing sales funnels. But what about the internal processes? Knowing who did what inside your HubSpot portal is just as critical, especially for accountability, audit trails, and seamless internal communications.
Recently, a fascinating discussion popped up in the HubSpot Community that perfectly illustrates this challenge. It’s a common scenario that many RevOps teams, marketers, and e-commerce managers face: needing to log the specific HubSpot user who performed an action, but hitting a snag with the API.
The HubSpot Community Problem: "Who Enabled Jira Access?"
The original poster in the community thread laid out their goal very clearly: when a specific contact property (jira_secured_performance_backlog_access) is set to "Yes," they wanted to automatically populate another field (jira_access_aangezet_door) with the First and Last Name of the user who made that change. This is brilliant for internal emails, audit logs, and ensuring everyone knows who to follow up with.
However, they ran into a wall. While their script successfully identified the sourceId (which is a HubSpot User ID) from the contact's property history, their subsequent API call to retrieve the user's name returned zero results. Their log snippet was quite telling:
INFO USER ID: 69604009 | INFO TOTAL USERS: 0 | INFO MATCH FOUND: false
They had enabled the right permissions (crm.objects.contacts.write, crm.objects.owners.read, settings.users.read, crm.objects.contacts.read) and tried different field types, but the user lookup just wasn't happening.
Cracking the Code: Owners vs. Users (and the Right API)
This is where things can get a little nuanced in HubSpot's API landscape. The sourceId you get from a property's history is indeed a HubSpot User ID. The challenge often comes in how you then try to translate that ID into a readable name.
While HubSpot does have a 'Users API' (under the Account API), for retrieving details about internal HubSpot users who are also 'owners' of CRM objects (like contacts, companies, deals), the CRM API | Owners endpoint is typically the most reliable and straightforward path. An ownerId is essentially a userId in this context, representing an internal HubSpot user who can be assigned ownership.
The original poster's INFO TOTAL USERS: 0 log likely indicates that their specific call to the 'Users API' was either using the wrong endpoint, or perhaps the userId wasn't being correctly recognized by that particular API, or the permissions weren't quite aligning with the exact endpoint they were hitting. The community moderator rightly pointed towards the CRM API | Owners documentation as a key resource, and that's exactly where we'll focus our solution.
Your Step-by-Step Solution with a HubSpot Custom Code Action
For ESHOPMAN users, this kind of precise tracking is invaluable. Here's how you can solve this using a HubSpot Workflow and a Custom Code Action:
Step 1: Set Up Your Workflow Trigger
- Create a new contact-based workflow.
- Set the enrollment trigger to when the property
jira_secured_performance_backlog_accessis known and has been updated to "Yes."
Step 2: Add a Custom Code Action
- After your trigger, add a "Custom code" action to your workflow. This is where the magic happens.
- You'll need to define input properties for this action. The most crucial one will be the
hs_object_id(the Contact ID) of the enrolled contact.
Step 3: Accessing Property History and the sourceId
Inside your Custom Code Action, you'll first need to make an API call back to HubSpot to fetch the property history for the specific field. This is how you'll get the sourceId.
// Example (Node.js for Custom Code Action)
const hubspot = require('@hubspot/api-client');
exports.main = async (event, callback) => {
const c
const propertyName = 'jira_secured_performance_backlog_access';
const hubspotClient = new hubspot.Client({
accessToken: process.env.YOUR_HUBSPOT_ACCESS_TOKEN // Use an environment variable for your token!
});
try {
// Get property history for the contact
const historyResp hubspotClient.crm.contacts.properties.get(contactId, propertyName, {
versions: true // Request property versions/history
});
let userId = null;
if (historyResponse.versions && historyResponse.versions.length > 0) {
// Find the most recent change where the value became 'Yes'
const latestChange = historyResponse.versions.find(v => v.value === 'Yes');
if (latestChange) {
userId = latestChange.sourceVid; // sourceVid is the user ID
}
}
if (userId) {
// ... proceed to fetch user details (Step 4)
} else {
callback({ outputFields: { jira_access_aangezet_door: 'N/A: User ID not found' } });
}
} catch (error) {
console.error('Error fetching property history:', error);
callback({ outputFields: { jira_access_aangezet_door: 'Error fetching history' } });
}
};
Step 4: Fetch User Details with the Owners API
Once you have the userId (which acts as an ownerId here), you'll make another API call, this time to the Owners API, to get the user's first and last name.
// ... (continued from Step 3 inside the `if (userId)` block)
const ownerResp hubspotClient.crm.owners.ownersApi.getById(userId);
const firstName = ownerResponse.firstName || '';
const lastName = ownerResponse.lastName || '';
const fullName = `${firstName} ${lastName}`.trim();
// ... (proceed to update the contact property - Step 5)
Step 5: Update the Contact Property
Finally, you'll update the target contact property (jira_access_aangezet_door) with the concatenated full name.
// ... (continued from Step 4)
// Set the output property for the Custom Code Action
callback({ outputFields: { jira_access_aangezet_door: fullName } });
} else {
callback({ outputFields: { jira_access_aangezet_door: 'N/A: User ID not found' } });
}
} catch (error) {
console.error('Error in custom code action:', error);
callback({ outputFields: { jira_access_aangezet_door: 'Error during processing' } });
}
};
A quick note on field types: The original poster tried both "Single-line text" and "HubSpot User" fields. If your goal is to store the actual "First Name Last Name" string, a "Single-line text" field is appropriate. If you wanted to link to the HubSpot user directly (which might be useful for reporting or segmentation), you'd typically set a "HubSpot User" field with the ownerId itself, but the request was for the name string.
Why This Matters for E-commerce & RevOps
This particular problem, while technical, highlights a crucial aspect of modern business operations: the need for granular data and accountability. For e-commerce businesses running on HubSpot, knowing who approved a discount, processed a return, or manually adjusted an order status can be invaluable for customer service, fraud prevention, and team performance analysis. Whether you're managing complex internal processes like Jira access, or integrating external systems – from bespoke CRMs to popular platforms like a building a Wix ecommerce website – into your HubSpot ecosystem, understanding who is doing what is paramount.
This level of detail helps prevent errors, streamlines communication, and provides a clear audit trail. It empowers your RevOps team to build more robust reporting and ensures your customer-facing teams have all the context they need.
ESHOPMAN Team Comment
We absolutely love this question from the community! It hits on a fundamental need for transparency and accountability within HubSpot-driven operations. While the solution requires a bit of API know-how, the approach of using Custom Code Actions to leverage HubSpot's Owners API is precisely what we'd recommend. It's a powerful way to bridge data gaps and create truly intelligent automations. Don't shy away from custom code when HubSpot's native features don't quite fit – it's often the key to unlocking next-level efficiency for your store.
The HubSpot Community is a treasure trove of insights, and this discussion is a perfect example of how diving into the technical details can unlock powerful new automations for your business. Don't let a small API hiccup deter you from building robust, transparent processes. With the right approach to HubSpot's APIs and a Custom Code Action, you can track virtually any internal action and make your HubSpot portal work even harder for your e-commerce business.