Customer Onboarding
Automate new customer setup across multiple systems
Customer Onboarding Tutorial
Learn how to automate new customer onboarding across multiple systems when a Stripe subscription is created, ensuring consistent setup and immediate engagement.
The Problem
When a new customer signs up, teams typically:
- Manually create the customer in the accounting system (QuickBooks/NetSuite)
- Add the contact to the CRM (HubSpot/Salesforce)
- Send a welcome email with setup instructions
- Notify the customer success team in Slack
- Create onboarding tasks or projects
- Update spreadsheets or dashboards
This manual process causes:
- Delayed onboarding - Hours or days before customer is fully set up
- Inconsistent data - Customer info varies across systems
- Missed steps - Someone forgets to update a system
- Poor first impression - Customer waits for manual actions
- Team bottlenecks - One person becomes the onboarding gatekeeper
The Solution
Build a workflow that automatically:
- Triggers when a Stripe subscription is created
- Creates the customer in QuickBooks for invoicing
- Creates or updates the contact in HubSpot as a customer
- Sends a personalized welcome email
- Notifies the customer success team in Slack
- (Optional) Creates an onboarding project or task
What You'll Build
Stripe Subscription Created
│
▼
┌───────────────────┐
│ Get Stripe │
│ Customer Details │
└─────────┬─────────┘
│
┌─────┴─────┐
▼ ▼
┌───────────┐ ┌───────────┐
│ Create │ │ Create/ │
│ QuickBooks│ │ Update │
│ Customer │ │ HubSpot │
└─────┬─────┘ └─────┬─────┘
└──────┬──────┘
│
▼
┌───────────────────┐
│ Send Welcome │
│ Email │
└─────────┬─────────┘
│
▼
┌───────────────────┐
│ Notify Team │
│ in Slack │
└───────────────────┘Prerequisites
Required Connections
| Provider | Connection Type | Purpose |
|---|---|---|
| Stripe | OAuth via Nango | Receive webhooks, get customer data |
| QuickBooks | OAuth via Nango | Create accounting customers |
| HubSpot | OAuth via Nango | Manage CRM contacts |
| Slack | OAuth via Nango | Team notifications |
| Email Provider | OAuth or API Key | Welcome emails |
Stripe Setup
-
Configure Webhook in Stripe Dashboard:
- Developers → Webhooks → Add endpoint
- URL:
https://your-domain.com/webhooks/stripe/{companyId} - Events:
customer.subscription.created
-
Note your webhook signing secret for verification
QuickBooks Setup
Ensure you have:
- An active QuickBooks Online account
- OAuth connection configured in Nango
HubSpot Setup
- Create Custom Properties (recommended):
stripe_customer_id(Single-line text)stripe_subscription_id(Single-line text)subscription_plan(Dropdown)mrr(Number - currency)
Step-by-Step Implementation
Step 1: Create the Workflow
Start with the Stripe webhook trigger:
{
"name": "Customer Onboarding",
"description": "Automate new customer setup when Stripe subscription is created",
"trigger": {
"type": "webhook",
"provider": "stripe",
"events": ["customer.subscription.created"]
},
"steps": []
}Step 2: Extract Customer Information
Parse the subscription data:
{
"id": "extract-customer-data",
"type": "transform",
"name": "Extract Customer Data",
"config": {
"operation": "map",
"data": {
"customerId": "{{input.data.object.customer}}",
"subscriptionId": "{{input.data.object.id}}",
"planName": "{{input.data.object.items.data.[0].plan.nickname}}",
"planAmount": "{{input.data.object.items.data.[0].plan.amount}}",
"interval": "{{input.data.object.items.data.[0].plan.interval}}",
"status": "{{input.data.object.status}}"
}
}
}Step 3: Get Full Customer Details from Stripe
Retrieve the complete customer profile:
{
"id": "get-stripe-customer",
"type": "action",
"name": "Get Stripe Customer Details",
"config": {
"connector": "stripe",
"action": "getCustomer",
"customerId": "{{steps.extract-customer-data.output.customerId}}"
}
}Output includes:
name- Customer nameemail- Email addressphone- Phone number (if provided)address- Billing addressmetadata- Custom fields
Step 4: Create QuickBooks Customer
Create the customer in your accounting system:
{
"id": "create-qb-customer",
"type": "action",
"name": "Create QuickBooks Customer",
"config": {
"connector": "quickbooks",
"action": "createCustomer",
"DisplayName": "{{steps.get-stripe-customer.output.name}}",
"CompanyName": "{{steps.get-stripe-customer.output.metadata.company_name}}",
"PrimaryEmailAddr": {
"Address": "{{steps.get-stripe-customer.output.email}}"
},
"PrimaryPhone": {
"FreeFormNumber": "{{steps.get-stripe-customer.output.phone}}"
},
"BillAddr": {
"Line1": "{{steps.get-stripe-customer.output.address.line1}}",
"City": "{{steps.get-stripe-customer.output.address.city}}",
"CountrySubDivisionCode": "{{steps.get-stripe-customer.output.address.state}}",
"PostalCode": "{{steps.get-stripe-customer.output.address.postal_code}}",
"Country": "{{steps.get-stripe-customer.output.address.country}}"
},
"Notes": "Stripe Customer ID: {{steps.get-stripe-customer.output.id}}\nSubscription: {{steps.extract-customer-data.output.planName}}"
},
"onError": {
"action": "continue",
"fallback": { "Id": null }
}
}Step 5: Create or Update HubSpot Contact
Add the customer to your CRM:
{
"id": "upsert-hubspot-contact",
"type": "action",
"name": "Create/Update HubSpot Contact",
"config": {
"connector": "hubspot",
"action": "createContact",
"email": "{{steps.get-stripe-customer.output.email}}",
"properties": {
"firstname": "{{steps.get-stripe-customer.output.name | split: ' ' | first}}",
"lastname": "{{steps.get-stripe-customer.output.name | split: ' ' | last}}",
"phone": "{{steps.get-stripe-customer.output.phone}}",
"company": "{{steps.get-stripe-customer.output.metadata.company_name}}",
"lifecyclestage": "customer",
"stripe_customer_id": "{{steps.get-stripe-customer.output.id}}",
"stripe_subscription_id": "{{steps.extract-customer-data.output.subscriptionId}}",
"subscription_plan": "{{steps.extract-customer-data.output.planName}}",
"mrr": "{{steps.extract-customer-data.output.planAmount | divided_by: 100}}"
}
},
"onError": {
"action": "continue",
"fallback": { "id": null }
}
}Key properties:
lifecyclestage: customer- Marks them as a paying customer- Custom Stripe fields for tracking
Step 6: Send Welcome Email
Send a personalized welcome email:
{
"id": "send-welcome-email",
"type": "email",
"name": "Send Welcome Email",
"config": {
"action": "send",
"provider": "sendgrid",
"to": "{{steps.get-stripe-customer.output.email}}",
"from": "onboarding@yourcompany.com",
"subject": "Welcome to {{steps.extract-customer-data.output.planName}}!",
"body": {
"html": "<h1>Welcome, {{steps.get-stripe-customer.output.name}}!</h1><p>Thank you for subscribing to <strong>{{steps.extract-customer-data.output.planName}}</strong>. We're excited to have you on board!</p><h2>Getting Started</h2><p>Here are your next steps:</p><ol><li><a href=\"https://app.yourcompany.com/onboarding\">Complete your profile setup</a></li><li><a href=\"https://docs.yourcompany.com/quickstart\">Read our quickstart guide</a></li><li><a href=\"https://yourcompany.com/book-call\">Book an onboarding call</a> with our team</li></ol><p>If you have any questions, reply to this email or reach out to <a href=\"mailto:support@yourcompany.com\">support@yourcompany.com</a>.</p><p>Welcome aboard!</p><p>The {{steps.extract-customer-data.output.planName}} Team</p>"
},
"template_variables": {
"customer_name": "{{steps.get-stripe-customer.output.name}}",
"plan_name": "{{steps.extract-customer-data.output.planName}}",
"app_url": "https://app.yourcompany.com"
}
}
}Step 7: Notify Customer Success Team
Alert the team about the new customer:
{
"id": "notify-cs-team",
"type": "slack",
"name": "Notify Customer Success",
"config": {
"action": "sendMessage",
"channel": "#new-customers",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "New Customer Signed Up!"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Customer:*\n{{steps.get-stripe-customer.output.name}}"
},
{
"type": "mrkdwn",
"text": "*Plan:*\n{{steps.extract-customer-data.output.planName}}"
},
{
"type": "mrkdwn",
"text": "*Email:*\n{{steps.get-stripe-customer.output.email}}"
},
{
"type": "mrkdwn",
"text": "*MRR:*\n${{steps.extract-customer-data.output.planAmount | divided_by: 100}}/{{steps.extract-customer-data.output.interval}}"
}
]
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "QuickBooks: {{#if steps.create-qb-customer.output.Id}}Created{{else}}Skipped{{/if}} | HubSpot: {{#if steps.upsert-hubspot-contact.output.id}}Created{{else}}Skipped{{/if}}"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View in Stripe"
},
"url": "https://dashboard.stripe.com/customers/{{steps.get-stripe-customer.output.id}}"
},
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View in HubSpot"
},
"url": "https://app.hubspot.com/contacts/YOUR_PORTAL_ID/contact/{{steps.upsert-hubspot-contact.output.id}}"
}
]
}
]
}
}Complete Workflow
Here's the complete workflow JSON:
{
"name": "Customer Onboarding",
"description": "Automate new customer setup when Stripe subscription is created",
"trigger": {
"type": "webhook",
"provider": "stripe",
"events": ["customer.subscription.created"]
},
"steps": [
{
"id": "extract-customer-data",
"type": "transform",
"name": "Extract Customer Data",
"config": {
"operation": "map",
"data": {
"customerId": "{{input.data.object.customer}}",
"subscriptionId": "{{input.data.object.id}}",
"planName": "{{input.data.object.items.data.[0].plan.nickname}}",
"planAmount": "{{input.data.object.items.data.[0].plan.amount}}",
"interval": "{{input.data.object.items.data.[0].plan.interval}}"
}
}
},
{
"id": "get-stripe-customer",
"type": "action",
"name": "Get Stripe Customer Details",
"config": {
"connector": "stripe",
"action": "getCustomer",
"customerId": "{{steps.extract-customer-data.output.customerId}}"
}
},
{
"id": "create-qb-customer",
"type": "action",
"name": "Create QuickBooks Customer",
"config": {
"connector": "quickbooks",
"action": "createCustomer",
"DisplayName": "{{steps.get-stripe-customer.output.name}}",
"PrimaryEmailAddr": {
"Address": "{{steps.get-stripe-customer.output.email}}"
},
"Notes": "Stripe ID: {{steps.get-stripe-customer.output.id}}"
},
"onError": {
"action": "continue",
"fallback": { "Id": null }
}
},
{
"id": "upsert-hubspot-contact",
"type": "action",
"name": "Create/Update HubSpot Contact",
"config": {
"connector": "hubspot",
"action": "createContact",
"email": "{{steps.get-stripe-customer.output.email}}",
"properties": {
"firstname": "{{steps.get-stripe-customer.output.name | split: ' ' | first}}",
"lastname": "{{steps.get-stripe-customer.output.name | split: ' ' | last}}",
"lifecyclestage": "customer",
"stripe_customer_id": "{{steps.get-stripe-customer.output.id}}",
"subscription_plan": "{{steps.extract-customer-data.output.planName}}"
}
},
"onError": {
"action": "continue",
"fallback": { "id": null }
}
},
{
"id": "send-welcome-email",
"type": "email",
"name": "Send Welcome Email",
"config": {
"action": "send",
"provider": "sendgrid",
"to": "{{steps.get-stripe-customer.output.email}}",
"from": "onboarding@yourcompany.com",
"subject": "Welcome to {{steps.extract-customer-data.output.planName}}!",
"body": {
"html": "<h1>Welcome!</h1><p>Thank you for subscribing. We're excited to have you on board!</p>"
}
}
},
{
"id": "notify-cs-team",
"type": "slack",
"name": "Notify Customer Success",
"config": {
"action": "sendMessage",
"channel": "#new-customers",
"blocks": [
{
"type": "header",
"text": { "type": "plain_text", "text": "New Customer Signed Up!" }
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*Customer:*\n{{steps.get-stripe-customer.output.name}}" },
{ "type": "mrkdwn", "text": "*Plan:*\n{{steps.extract-customer-data.output.planName}}" }
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": { "type": "plain_text", "text": "View in Stripe" },
"url": "https://dashboard.stripe.com/customers/{{steps.get-stripe-customer.output.id}}"
}
]
}
]
}
}
]
}Testing
1. Create Test Workflow
curl -X POST http://localhost:3000/api/v1/workflows \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d @workflow.json2. Activate Workflow
curl -X POST http://localhost:3000/api/v1/workflows/{workflowId}/activate \
-H "x-api-key: YOUR_API_KEY"3. Test with Simulated Webhook
curl -X POST http://localhost:3000/webhooks/stripe/{companyId} \
-H "Content-Type: application/json" \
-H "stripe-signature: test" \
-d '{
"type": "customer.subscription.created",
"data": {
"object": {
"id": "sub_1234567890",
"customer": "cus_1234567890",
"status": "active",
"items": {
"data": [{
"plan": {
"nickname": "Pro Plan",
"amount": 9900,
"interval": "month"
}
}]
}
}
}
}'4. Verify Results
| System | What to Verify |
|---|---|
| QuickBooks | New customer created with Stripe ID in notes |
| HubSpot | Contact created with lifecycle stage = customer |
| Welcome email received | |
| Slack | Notification in #new-customers |
5. End-to-End Test
- Create a test product and price in Stripe
- Use Stripe test mode to create a subscription
- Watch the workflow execute automatically
- Verify all systems updated
Error Handling
Graceful Degradation
The workflow uses onError: { action: "continue" } for non-critical steps so the workflow continues even if one system fails:
{
"id": "create-qb-customer",
"config": { ... },
"onError": {
"action": "continue",
"fallback": { "Id": null }
}
}Notify on Partial Failure
Add a check at the end:
{
"id": "check-all-succeeded",
"type": "condition",
"config": {
"if": "{{steps.create-qb-customer.output.Id && steps.upsert-hubspot-contact.output.id}}",
"then": "end",
"else": "notify-partial-failure"
}
},
{
"id": "notify-partial-failure",
"type": "slack",
"config": {
"action": "sendMessage",
"channel": "#ops-alerts",
"text": ":warning: Customer onboarding partially failed for {{steps.get-stripe-customer.output.email}}. Check QuickBooks: {{#unless steps.create-qb-customer.output.Id}}FAILED{{/unless}}, HubSpot: {{#unless steps.upsert-hubspot-contact.output.id}}FAILED{{/unless}}"
}
}Handle Duplicate Customers
Check if customer already exists:
{
"id": "check-qb-customer-exists",
"type": "action",
"config": {
"connector": "quickbooks",
"action": "findCustomerByEmail",
"email": "{{steps.get-stripe-customer.output.email}}"
}
},
{
"id": "decide-create-or-skip",
"type": "condition",
"config": {
"if": "{{steps.check-qb-customer-exists.output.QueryResponse.Customer.length > 0}}",
"then": "skip-qb-creation",
"else": "create-qb-customer"
}
}Variations and Extensions
Plan-Specific Onboarding
Different onboarding flows for different plans:
{
"id": "route-by-plan",
"type": "condition",
"config": {
"if": "{{steps.extract-customer-data.output.planName === 'Enterprise'}}",
"then": "enterprise-onboarding",
"else": {
"if": "{{steps.extract-customer-data.output.planName === 'Pro'}}",
"then": "pro-onboarding",
"else": "basic-onboarding"
}
}
}Create NetSuite Customer Instead
For enterprise companies using NetSuite:
{
"id": "create-netsuite-customer",
"type": "action",
"config": {
"connector": "netsuite",
"action": "createCustomer",
"companyName": "{{steps.get-stripe-customer.output.metadata.company_name}}",
"email": "{{steps.get-stripe-customer.output.email}}",
"subsidiary": { "id": "1" },
"customFields": {
"custentity_stripe_id": "{{steps.get-stripe-customer.output.id}}"
}
}
}Scheduled Follow-up Email
Send a check-in email 7 days after signup:
{
"id": "schedule-followup",
"type": "email",
"config": {
"action": "schedule",
"provider": "resend",
"to": "{{steps.get-stripe-customer.output.email}}",
"subject": "How's it going with {{steps.extract-customer-data.output.planName}}?",
"body": {
"html": "<p>Hi {{steps.get-stripe-customer.output.name}},</p><p>You've been using {{steps.extract-customer-data.output.planName}} for a week now. How's it going?</p><p>If you have any questions or need help, just reply to this email!</p>"
},
"send_at": "{{now | dateAdd: 7, 'days' | date: '%Y-%m-%dT%H:%M:%SZ'}}"
}
}Create Salesforce Account and Contact
For Salesforce users:
{
"id": "create-sf-account",
"type": "action",
"config": {
"connector": "salesforce",
"action": "createOpportunity",
"Name": "{{steps.get-stripe-customer.output.metadata.company_name}} - New Subscription",
"StageName": "Closed Won",
"CloseDate": "{{now | date: '%Y-%m-%d'}}",
"Amount": "{{steps.extract-customer-data.output.planAmount | divided_by: 100}}"
}
},
{
"id": "create-sf-contact",
"type": "action",
"config": {
"connector": "salesforce",
"action": "createContact",
"FirstName": "{{steps.get-stripe-customer.output.name | split: ' ' | first}}",
"LastName": "{{steps.get-stripe-customer.output.name | split: ' ' | last}}",
"Email": "{{steps.get-stripe-customer.output.email}}",
"Stripe_Customer_ID__c": "{{steps.get-stripe-customer.output.id}}"
}
}Assign CSM Based on Plan
Route high-value customers to senior CSMs:
{
"id": "determine-csm",
"type": "transform",
"config": {
"operation": "map",
"data": {
"csmEmail": "{{#if (gt steps.extract-customer-data.output.planAmount 50000)}}senior-csm@company.com{{else if (gt steps.extract-customer-data.output.planAmount 10000)}}csm@company.com{{else}}onboarding@company.com{{/if}}",
"csmSlackId": "{{#if (gt steps.extract-customer-data.output.planAmount 50000)}}U12345{{else}}U67890{{/if}}"
}
}
},
{
"id": "notify-assigned-csm",
"type": "slack",
"config": {
"action": "sendMessage",
"channel": "@{{steps.determine-csm.output.csmSlackId}}",
"text": "You've been assigned a new customer: {{steps.get-stripe-customer.output.name}} ({{steps.extract-customer-data.output.planName}})"
}
}Trial Conversion Variant
Handle trial-to-paid conversion differently:
{
"trigger": {
"type": "webhook",
"provider": "stripe",
"events": ["customer.subscription.updated"],
"filter": {
"data.object.status": { "$eq": "active" },
"data.previous_attributes.status": { "$eq": "trialing" }
}
}
}Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Duplicate customers | Same webhook fired twice | Add idempotency check with subscription ID |
| Missing customer name | Stripe customer has no name | Fall back to email prefix |
| QuickBooks sync failed | Customer already exists | Check before create, update instead |
| Email not sent | Invalid email format | Validate email in transform step |
| Wrong plan amount | Amount is in cents | Divide by 100 for display |