Sales Hub and Customer Service Hub are both Microsoft model-driven apps sitting on the same Dataverse tables. They look like they should coexist effortlessly. They do not, because each app encodes assumptions about how the underlying data is used.
The shared table list
Account, Contact, Activity, Email, Phone Call, Task, Lead. Both apps read and write all of these. Customizations applied to support one app’s workflow often break the other’s.
Pattern 1: account ownership
Sales Hub assumes account ownership equals “the rep who hunts this account.” Customer Service Hub assumes ownership is “the agent or queue currently handling support.” If both apps live in the same environment with no governance, accounts get reassigned every time a service ticket opens.
Fix: use ownership for sales and put the service queue on a related table (incident.owningqueue), not on the account.
Pattern 2: contact roles
Sales tracks “decision maker, influencer, blocker.” Service tracks “primary contact for billing, technical contact.” Both stuff the data into the same Contact and use different custom fields. Six months in, the contact form has 40 fields, half of which each team ignores.
Fix: separate the roles into a related Contact Role table (accountid, contactid, role, context) so each team manages its own without bloating the contact form.
Pattern 3: activity lifecycle
Sales activities follow a “next step” cadence and complete naturally. Service activities follow SLA timers and are gated by case status. The default activity timeline shows both interleaved, which confuses both audiences.
Fix: filter the activity timeline per app via RegardingObjectId type and add an activitysubtype discriminator so each app’s timeline shows only relevant entries.
Pattern 4: form layout battles
The Account: Main form is the same form across both apps unless you create app-specific forms. Sales wants revenue, opportunities, deal velocity. Service wants entitlements, active cases, SLA status.
Fix: in each app’s app designer, assign a different main form per app. Same table, two forms, no cross-team friction.
App Designer -> Sales Hub -> Account -> Form: Account-Sales
App Designer -> Customer Service Hub -> Account -> Form: Account-Service
Pattern 5: business process flows
Each app has its own BPF on shared tables. By default, only one BPF can be active per record at a time. If a record has both an open opportunity and an open case, the BPF on the account is whichever was applied last.
Fix: put the BPFs on the child tables (Opportunity, Incident), not on the parent. The parent stays clean.
What to do this week
Open the Account main form. Count fields. If you cannot explain to a junior analyst within 30 seconds why each one is there, your form has been a battleground. Split into app-specific forms before adding the next field.