# Hubspot with Automation Events
## Summary
The document discusses two methods for handling form submissions and automation processes using HubSpot, Segment, and an automation server.
1) The first method, "Form Intercept," involves intercepting form submissions and sending them to the automation server for processing.
2) The second method, "Segment Webhook," involves using Segment's data layer to capture form submissions and send them to the automation server via a webhook.
The document provides information about the events that occur during the automation process, including form events, contact and account-level events, and page load events. It also discusses the importance of syncing custom traits to HubSpot and introduces the recommended trait "action_filter" for filtering form submissions.
The document include diagrams that illustrate the workflow process and the validation of incoming events. It also provides Zod schemas for validating incoming form submission payloads and outgoing track events related to automation.
**The schemas ensure that the data is correctly structured and includes all necessary fields.**
The document highlights potential risks associated with each method, such as client tracking being obstructed by ad-blockers or data being dropped during the automation process. It also provides code examples and explanations for using the Segment Analytics library and Zod schema validation.
Overall, the document serves as a comprehensive guide for implementing form submission and automation processes using HubSpot, Segment, and an automation server, with a focus on data validation and event tracking.
# Table of Contents
1. [Options](#options)
- [Workflow Process](#workflow-process)
- [Validate Incoming Events](#validate-incoming-events)
2. [Method 1: Form Intercept](#method-1-form-intercept)
- [Forms API - POST URL](#forms-api---post-url)
- [Risks](#risks)
3. [Method 2: Segment Webhook](#method-2-segment-webhook)
- [Risks](#risks-1)
4. [Events Information](#events-information)
- [Form Events](#form-events)
- [Syncing Custom Traits to HubSpot](#syncing-custom-traits-to-hubspot)
- [Recommended Trait: action_filter (Field Label)](#recommended-trait-action_filter-field-label)
5. [Validation: Outgoing Track Event (Automation)](#validation-outgoing-track-event-automation)
6. [Validation: Incoming Payload (Form Submission)](#validation-incoming-payload-form-submission)
# Options
## Workflow Process
```mermaid
sequenceDiagram
participant Client
participant Segment
participant AutomationServer
Client->>Segment: Form Submission (Form Data)
Segment->>AutomationServer: Webhook (Form Data)
AutomationServer->>AutomationServer: Process Data
AutomationServer->>Segment: Track Event (Automation)
Segment->>Client: Update Client State (Optional)
Note over AutomationServer: Validation using Zod Schema
```
## Validate incoming events
```mermaid
sequenceDiagram
participant AutomationServer as Automation Server
participant ZodSchema as Zod Schema
participant Segment
AutomationServer->>ZodSchema: Validate Incoming Event
ZodSchema->>AutomationServer: ValidationResult
alt Validation Success
AutomationServer->>AutomationServer: Process Data
AutomationServer->>Segment: Track Event (Automation)
else Validation Failed
AutomationServer->>AutomationServer: Handle Error
end
```
# OPTIONS
**METHOD 1**
### Form Intercept
Hubspot forms work multiple ways you can either let the submission go direct to hubspot or replace the POST url with a hook for the automation process.
Meanwhile the data can still pass into Hubspot in two ways which are data layer integration via segment event with an idenittiy + group call or through attribute tracking on the form node elements using hubspot tracking js which is already active via segment globally on the site.
### Forms API - POST URL
Each Hubspot form has a unique ID and POST URL you can submit to this by using a typical post request and form data payload that models the same sent by the form.
It typically uses the same data model as the form object in hubspot which represents the object key value pair associated with the contact and account objects.
**RISK**: Client tracking can be obstructed but no critical risks
- Update data is dropped during automation process (non-critical)
- Client Tracking Blocked due to Ad-blocker
- Client Segment Script is blocked due to ad-blocker
[Once on WP site this is a non-issue due to server side events]
---
**METHOD 2**
## Segment Webhook
All form and submissions already submit into Segment's data layer, those submissions can be posted to the automation server allowing for processing. Once complete, you can post the data back to Segment via server side event.
This is the recommended option that will provide the most consistent and stable results.
**RISK**: Client tracking can be obstructed but no critical risks
- Update data is dropped during automation process (non-critical)
- Client Tracking Blocked due to Ad-blocker
- Client Segment Script is blocked due to ad-blocker
[Once on WP site this is a non-issue due to server side events]
# Events Information
Segment event information for new `Automation Request` pipeline
```javascript=
// FORM EVENTS
// These are filled with FORM Data via Hubspot Form + Segment
//Contact Level
analytics.identify('user1234', {
email: 'petergibbon@email.com',
firstname: 'Peter',
lastname: 'Gibbon'
})
// Account Level
analytics.group({
groupId: "some_group_id",
userId: "some_user_id",
traits: {
website: "https://www.example.com",
name: "Example Inc."
}
});
// Page Load Event
analytics.page()
// Form Loaded Event
analytics.ready(function(){
hbspt.forms.create({
portalId: '',
formId: '',
css: '',
cssRequired: '',
});
})
```
### Syncing Custom Traits to HubSpot
HubSpot requires that you create and define any custom traits in the HubSpot UI before you send any Engage data to HubSpot. If you try to sync a property from Segment that does not exist in HubSpot, the sync fails.
#### Recommended Trait: [action_filter] (Field Label)
New hidden field added to forms that allows for easy filtering via Segment. Whenever an action filter has a URL (https://domain.com/webhook/endpoint) segment will POST the data to a source where it will be process and submitted via track events using the associated group / userId.
[Hubspot + Segment Documentation](https://segment.com/docs/connections/destinations/catalog/hubspot/)
[CLOUD API: HUBSPOT](https://segment.com/docs/connections/destinations/catalog/actions-hubspot-cloud/)
```javascript
analytics.track("Automation", {
userId: "AiUGstSDIg",
type: "CWV Report",
status: "PEDNING",
success: false
});
```
```json
// INCOMING FORM SUBMISSION WEBHOOK BODY
{
"type": "track",
"event": "Form Submission",
"userId": "user123",
"properties": {
"firstName": "John",
"lastName": "Doe",
"company": "Acme Inc.",
"website": "https://www.acme.com",
"email": "john.doe@acme.com"
}
}
// ZOD SCHEMA VALIDATION BELOW FOR BOTH
// OUTGOING TRACK EVENT PAYLOAD
{
"type": "track",
"event": "Automation",
"properties": {
"groupId": "asda123"
"userId": "AiUGstSDIg",
"type": "CWV Report",
"status": "PEDNING",
"success": false
}
}
```
Overall for the automation server the only requirements are an endpoint to catch `POST` from segment, which have been pre-filtered using the `action_filter` label and custom property on the Hubspot forms API.
In-addition, instead of sending directly via Hubspot API there should instead be an event stream:
- Automation [Event Name]
properties:
- groupId: this is the account ID
- userId: this is the contact's ID
- type: The automation event that has occured
- status: ENUM providing a status of the current request
(useful for back populating and updating)
- succes: Did the event process finish?
Library to use for Automation Servers:
**NPM**: Segement https://www.npmjs.com/package/@analytics/segment
Track events should be the only event needed in most cases unless there is additional custom workflows due to being passed the `userID` and `groupID` on the `action_filtered` `POST` request.
The following is the recommend event schema using Zod + Typescript:
## VALIDATION: OUTGOING TRACK EVENT [AUTOMATION]
```typescript=
// Import Zod library
import { z } from 'zod';
// Define an enumeration for the status field
const AutomationStatus = z.enum(['PENDING', 'IN_PROGRESS', 'COMPLETED', 'FAILED']);
// Define the Automation Event schema using Zod
const AutomationEventSchema = z.object({
// The event name, which should be "Automation" for this schema
event: z.literal('Automation'),
// The properties object containing additional details about the event
properties: z.object({
// The account ID associated with the event
groupId: z.string().nonempty().comment('The account ID associated with the event'),
// The contact's ID associated with the event
userId: z.string().nonempty().comment('The contact\'s ID associated with the event'),
// The type of automation event that has occurred
type: z.string().nonempty().comment('The type of automation event that has occurred'),
// The status of the current request (e.g., PENDING, IN_PROGRESS, COMPLETED, FAILED)
status: AutomationStatus.comment('The status of the current request'),
// A boolean indicating whether the event processing finished successfully
success: z.boolean().comment('A boolean indicating whether the event processing finished successfully'),
}),
});
// Example usage of the Zod schema with Segment Analytics event
const segmentEvent = {
type: 'track',
event: 'Automation',
properties: {
groupId: 'asda123',
userId: 'AiUGstSDIg',
type: 'CWV Report',
status: 'PENDING',
success: false,
},
};
// Validate the event using the Zod schema
const validationResult = AutomationEventSchema.safeParse(segmentEvent);
// Check if the validation was successful
if (validationResult.success) {
// If successful, extract the validated data
const validatedEvent = validationResult.data;
// Use the validated data to send the event to Segment
analytics.track(validatedEvent.event, validatedEvent.properties);
} else {
// If validation failed, handle the error (e.g., log the error, return an error response, etc.)
console.error('Event validation failed:', validationResult.error);
}
```
## VALIDATION: INCOMING PAYLOAD [FORM SUBMISSION]
```typescript=
// Import Zod library
import { z } from 'zod';
// Define the schema for the form submission track event
const FormSubmissionTrackEventSchema = z.object({
// The type of the event, which should be "track" for this schema
type: z.literal('track'),
// The event name, which should be "Form Submission" for this schema
event: z.literal('Form Submission'),
// The unique identifier for the user who submitted the form
userId: z.string().nonempty(),
// The properties object containing the form field values
properties: z.object({
// The first name of the user who submitted the form
firstName: z.string().nonempty().comment('The first name of the user who submitted the form'),
// The last name of the user who submitted the form
lastName: z.string().nonempty().comment('The last name of the user who submitted the form'),
// The company name provided in the form
company: z.string().nonempty().comment('The company name provided in the form'),
// The website URL provided in the form
website: z.string().nonempty().url().comment('The website URL provided in the form'),
// The email address of the user who submitted the form
email: z.string().nonempty().email().comment('The email address of the user who submitted the form'),
}),
});
// Example usage of the Zod schema
const trackEvent = {
type: 'track',
event: 'Form Submission',
userId: 'user123',
properties: {
firstName: 'John',
lastName: 'Doe',
company: 'Acme Inc.',
website: 'https://www.acme.com',
email: 'john.doe@acme.com',
},
};
// Validate the event using the Zod schema
const validationResult = FormSubmissionTrackEventSchema.safeParse(trackEvent);
// Check if the validation was successful
if (validationResult.success) {
// If successful, extract the validated data
const validatedEvent = validationResult.data;
// Use the validated data (e.g., send the event to Segment, process the form submission, etc.)
console.log('Validated event:', validatedEvent);
} else {
// If validation failed, handle the error (e.g., log the error, return an error response, etc.)
console.error('Event validation failed:', validationResult.error);
}
```