Campaign attribution to CRM: webhook setup for Salesforce and HubSpot
Map SkipUp's meeting webhook to Salesforce Campaign Members or HubSpot Deals for campaign attribution. Step-by-step Zapier recipes with field mappings.
TL;DR: SkipUp fires a webhook on every campaign meeting booking. That webhook carries the campaign source, attendee, meeting time, and assigned rep — everything your CRM needs to attribute the meeting to the right campaign. This tutorial walks through catching that webhook with Zapier and mapping it to Salesforce Campaign Members or HubSpot Deals. No native CRM connector required. No custom code. The same approach works with Make or n8n if Zapier is not your middleware of choice.
Key Facts:
- SkipUp sends a
meeting_bookedwebhook (also called an event notification or HTTP callback) containing campaign source, attendee details, meeting time, and assigned rep for every campaign meeting booked through a campaign email address.- CRM sync is webhook-based — SkipUp does not write directly to Salesforce or HubSpot APIs. Middleware (Zapier, Make, or n8n) catches the webhook and maps fields to CRM objects.
- For Salesforce, the target object is a CampaignMember record linked to the source Campaign with status “Meeting Booked.” For HubSpot, the target is a Deal associated with the Contact and a Meeting engagement logged on the Contact timeline.
- Campaign attribution through webhooks is single-touch: each booked meeting attributes to the campaign email address the prospect responded to. This is the same email-based attribution model used across all SkipUp campaigns.
- Prerequisites: SkipUp account with webhooks enabled, Zapier Starter plan or higher (or Make/n8n equivalent), Salesforce Enterprise Edition or higher (API access requires Enterprise), HubSpot Sales Hub Professional or higher.
What does the webhook payload contain?
Every campaign meeting booked through SkipUp triggers a meeting_booked event sent to your configured webhook URL. Campaign attribution starts here: the payload carries the fields your CRM needs to create an attributed record.
Representative payload:
{
"event": "meeting_booked",
"campaign_id": "camp_abc123",
"campaign_name": "NAA 2026",
"attendee_email": "[email protected]",
"attendee_name": "Sarah Chen",
"meeting_time": "2026-03-15T14:00:00Z",
"assigned_rep_email": "[email protected]",
"assigned_rep_name": "Marcus Rivera",
"meeting_type": "campaign_meeting",
"booking_source": "email"
}
This is a representative payload. Check SkipUp’s API documentation for current field names and additional fields.
The campaign_id field is the mapping anchor. Use it — not campaign_name — when looking up CRM Campaign records. Display names with trailing spaces or special characters cause silent lookup failures. Internal IDs do not.
Every field in this payload flows downstream into your CRM. The sections below show exactly where each field lands in Salesforce and HubSpot.
How do you map the webhook to Salesforce Campaign Members?
The goal is a CampaignMember record in Salesforce that links the prospect to the source campaign with a status of “Meeting Booked.” Four Zapier actions get you there.
Trigger: Webhooks by Zapier → Catch Hook. This creates a unique URL. Register that URL in SkipUp’s webhook settings for the meeting_booked event. No native SkipUp Zapier app trigger is needed — the generic Catch Hook receives any HTTP POST.
Action 1: Find the Contact. Salesforce → Find Record. Search the Contact object by Email = attendee_email. If a match exists, you have the ContactId for the CampaignMember record.
Action 2: Create a Lead if needed. Salesforce → Create Record (conditional, runs only if Action 1 returns no match). Object: Lead. Map attendee_email to Email. The attendee_name field arrives as a single string, but Salesforce requires separate FirstName and LastName fields. Add a Formatter by Zapier → Split Text step: split on space, map the first segment to FirstName and the last segment to LastName. Set LeadSource to “Campaign Email.”
Action 3: Find the Salesforce Campaign. Salesforce → Find Record. Object: Campaign. SkipUp’s campaign_id is an internal identifier, not a Salesforce CampaignId. This step resolves the mapping. Two approaches work: match on a custom External ID field (SkipUp_Campaign_ID__c = campaign_id) for reliable, case-insensitive lookups, or match on Campaign Name = campaign_name if you prefer simplicity over resilience. The External ID approach is recommended. The output is the Salesforce CampaignId used in Action 4.
Action 4: Create the CampaignMember. Salesforce → Create Record. Object: CampaignMember. Map ContactId (or LeadId from Action 2) and CampaignId from Action 3. Set Status to “Meeting Booked.” If “Meeting Booked” does not exist in your CampaignMemberStatus picklist, add it in Salesforce Setup → Campaign Member Statuses before running the Zap.
Priya runs RevOps at a mid-market SaaS company with eight active campaigns across trade shows, webinars, and partner referrals. Before this integration, her reps manually tagged Campaign Members in Salesforce after each meeting — when they remembered. Compliance hovered around 40%. The campaign_id field in SkipUp’s webhook resolved the consistency problem. Every booked meeting now creates a CampaignMember automatically, and her campaign analytics dashboard shows booking rates per campaign without relying on rep data entry.
Field mapping reference:
| Webhook field | Salesforce object | Salesforce field | Notes |
|---|---|---|---|
campaign_id | Campaign (via lookup) | SkipUp_Campaign_ID__c (custom External ID) | Look up Campaign in Action 3; do not map directly |
attendee_email | Contact or Lead | Find-or-create pattern | |
attendee_name | Contact or Lead | FirstName, LastName | Parse with Formatter → Split Text |
assigned_rep_email | Contact or Lead | OwnerId | Optional: look up User by email |
meeting_time | Event (optional) | StartDateTime | Optional: log as activity |
The same webhook data maps to HubSpot, though the CRM objects differ: HubSpot uses Deal properties and Contact associations where Salesforce uses purpose-built CampaignMember records. The field mappings below show the HubSpot-specific path.
How do you map the webhook to HubSpot Deals for campaign attribution?
In HubSpot, the attribution path runs through Deals associated with Contacts. The webhook creates a Deal tied to the campaign source and logs a Meeting engagement on the Contact timeline. Before running this Zap, create the custom properties it references: Lead Source on the Contact object and Campaign Name and Campaign ID on the Deal object. Both are created in HubSpot Settings → Properties.
Trigger: Same Webhooks by Zapier Catch Hook URL. One webhook endpoint can feed both CRMs if you use Zapier Paths (available on any paid plan), or use separate Zaps.
Action 1: Find the Contact. HubSpot → Find Contact by attendee_email.
Action 2: Create or update the Contact. HubSpot → Create or Update Contact. Use “Create or Update” (not bare “Create”) to avoid duplicate records when the same prospect books multiple campaign meetings. Map attendee_email, parse attendee_name into First Name and Last Name fields using Formatter → Split Text (same approach as the Salesforce recipe), and set a custom Lead Source property to the campaign_name. For the reverse use case (triggering SkipUp meetings from HubSpot form submissions), see automate meeting scheduling from HubSpot form submissions.
Action 3: Create a Deal. HubSpot → Create Deal. Map Deal Name to a combination of attendee name and campaign name. Set the pipeline stage to “Meeting Booked” and use the “Associate Deal With” field to link the Contact ID from Action 2. HubSpot requires the Contact ID for association — the Contact must exist before the Deal can reference it. Map campaign_name and campaign_id to custom Deal properties for campaign attribution reporting.
Action 4: Log the Meeting. HubSpot → Create Engagement (type: Meeting). Map meeting_time to the engagement timestamp and associate with the Contact. This surfaces the booked meeting on the Contact’s activity timeline, giving sales reps context before the call. This action requires HubSpot Sales Hub Professional or higher. If your plan does not include Meeting engagements, skip this action — the Deal from Action 3 still provides campaign attribution.
Field mapping reference:
| Webhook field | HubSpot object | HubSpot property | Notes |
|---|---|---|---|
attendee_email | Contact | Email (built-in) | Find-or-create in Actions 1–2 |
attendee_name | Contact | First Name, Last Name (built-in) | Parse with Formatter → Split Text |
campaign_name | Contact | Lead Source (custom property) | Create in HubSpot Settings if not present |
campaign_name | Deal | Campaign Name (custom property) | For campaign attribution reporting |
campaign_id | Deal | Campaign ID (custom property) | For programmatic lookups |
meeting_time | Engagement (Meeting) | Timestamp (built-in) | ISO 8601 format |
assigned_rep_email | Deal | Deal Owner (built-in) | Optional: look up HubSpot User by email |
Teams running partner referral programs can use the identical webhook-to-Deal pattern. The campaign_id field works the same way whether the campaign source is a trade show, a webinar, or a partner email address. Attribution flows through Deal properties and Contact associations, which means reporting in HubSpot rolls up naturally through deal pipelines and contact lists.
What if you use Make or n8n instead of Zapier?
The webhook is a standard HTTP POST. Any middleware that can receive an HTTP request and call CRM APIs can replace Zapier. For a general guide to Zapier integration with SkipUp, see automate meeting scheduling with Zapier. The recipes below adapt the same campaign attribution pattern for alternative platforms.
Make uses a Custom Webhook module as the trigger — functionally equivalent to Zapier’s Catch Hook. Salesforce and HubSpot modules map the same fields to the same CRM objects. Make’s visual router adds one capability not available in Zapier’s base paid tier: conditional routing. A single webhook can route to Salesforce or HubSpot based on a field value, which matters if your organization runs both CRMs across different business units. Make charges by operation count rather than per-task, which can reduce cost at high webhook volumes.
n8n uses a Webhook trigger node with the same POST reception. Salesforce and HubSpot nodes provide equivalent actions. n8n can be self-hosted (Docker or npm) or used via n8n Cloud. Self-hosted carries no per-execution fees and gives full control over error handling and retry logic. n8n Cloud uses usage-based pricing with a managed hosting layer. For teams processing hundreds of campaign bookings monthly, either option avoids the per-task cost structure of Zapier and Make.
Both alternatives follow the same pattern: catch the webhook, find or create the contact, create the attributed CRM record. The middleware changes. The field mappings do not.
How do you verify campaign attribution is flowing to your CRM?
Seven failure modes cover the majority of webhook-to-CRM integration issues. Most surface during the first test booking, not in production.
Webhook not firing: SkipUp’s webhook settings must have the Catch Hook URL saved and the meeting_booked event enabled. Test by creating a campaign booking and checking the Zapier task history for the incoming payload.
Authentication expired: Zapier and Make CRM connections require periodic re-authentication. Salesforce OAuth tokens expire based on your org’s session settings. Set a calendar reminder for 90-day re-auth cycles — a missed re-auth silently drops every booking until someone notices the CRM gap.
Duplicate contacts: Always use a “Find or Create” pattern. A bare “Create Contact” action generates duplicates when the same prospect books through multiple campaigns. The find step costs one additional API call per webhook: a negligible price for data integrity.
Campaign lookup returns no results: If you use the External ID approach for Salesforce, verify that SkipUp_Campaign_ID__c is populated on the Campaign record. If you match by Campaign Name, verify the name matches exactly — trailing spaces, ampersands, or renamed campaigns break the lookup silently. In both cases, test with a known campaign before going live.
HubSpot custom properties missing: The Zap will fail on the first run if the custom properties (Lead Source on Contact, Campaign Name and Campaign ID on Deal) do not exist in HubSpot. Create them in HubSpot Settings → Properties before enabling the Zap.
HubSpot association order: Deals require an existing Contact ID for association. If your Zap creates the Deal before the Contact exists, the association fails. Structure your Zap so Contact creation always precedes Deal creation.
Field mapping case sensitivity: Webhook field names are case-sensitive. attendee_email is not Attendee_Email. Copy field names directly from the webhook payload — do not retype them.
Start with one campaign. Register the Catch Hook URL, trigger a test booking, and verify the CampaignMember (Salesforce) or Deal (HubSpot) appears with the correct campaign attribution, attendee data, and status. Once the first record flows through, extend to additional campaigns. From that point forward, the webhook fires on every campaign lead conversion, the middleware maps the fields, and the CRM record appears automatically.
Let's automate
your scheduling
Spend less time updating tools and more time closing deals.
Free for your first 10 meetings. No credit card required.