Back to Client / Customer Onboarding

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Client / Customer Onboarding Automation

[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]

This document is the primary technical reference for the FullSpec team building the Client Onboarding automation. It covers the current-state step map, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. Everything required to build, wire, and test the automation is contained here. The process owner's responsibilities are limited to confirming tool credentials, approving the Typeform field-to-HubSpot property mapping before build starts, and signing off on QA results.

01Process snapshot

Step
Name
Description
1
Confirm Contract Signed and Deal Closed
Account Manager checks DocuSign or HubSpot to confirm the deal is Closed Won. Reactive, not scheduled. 10 min.
2
Send Welcome Email to Client
Welcome email drafted and sent manually from Gmail using a saved template requiring per-client edits. Personalisation errors are common. 15 min.
3
Send Intake Form to Collect Client Details
Typeform intake link emailed to client. Staff must manually track submissions and flag non-responders. 10 min. BOTTLENECK.
4
Chase Incomplete Intake Form Responses
Follow-up email sent manually if the form is not returned within two to three days. Frequently forgotten, stalling all downstream steps. 10 min. BOTTLENECK.
5
Create Client Record in CRM
Intake data manually re-entered into HubSpot contact and company fields by the Operations Coordinator. Error-prone. 15 min.
6
Set Up Client Account in Service Platform
Operations team provisions client access, assigns roles, and configures settings based on service tier. Requires human judgement. 20 min.
7
Create and Send First Invoice
Finance raises invoice in Xero using deal value, billing contact from intake form, and contracted payment terms. Cross-checking required. 15 min.
8
Notify Internal Team in Slack
Account Manager posts a message to the delivery channel. Often delayed or skipped under workload. 5 min.
9
Schedule Kickoff Call
Kickoff invitation sent via Gmail with a calendar or Calendly link. Blocked until intake and account setup are confirmed. 10 min.
10
Confirm Onboarding Complete and Update CRM
HubSpot deal stage updated to Onboarded and a completion note logged. Frequently skipped or delayed. 10 min.
Time cost summary: Total manual time per onboarding cycle is 120 minutes (2 hours). At approximately 20 new clients per month that is 40 hours/month, or roughly 10 hours/week against a five-day working week. The template snapshot records 5 manual hours/week as the loaded figure after accounting for cycle variance. The automation replaces steps 1, 2, 3, 4, 5, 7, 8, and 10. Steps 6 (account provisioning) and 9 (kickoff scheduling) remain manual.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Onboarding Coordinator Agent

The Onboarding Coordinator Agent is the primary automation actor in this flow. It listens for the HubSpot deal stage change to Closed Won and then orchestrates the full welcome and data-collection sequence in order: sending the personalised welcome email via Gmail, dispatching the Typeform intake link, monitoring for form submission with an automated 48-hour reminder fallback, mapping all submitted intake fields to the correct HubSpot contact and company properties, creating and sending the first invoice in Xero, and finally updating the HubSpot deal stage to Onboarded with a timestamped completion note. The agent handles the majority of the onboarding chain without any human intervention and must be resilient to missing intake data and Xero contact mismatches.

Trigger
HubSpot deal stage changes to Closed Won (webhook or polling interval not greater than 5 minutes)
Tools
HubSpot, Gmail, Typeform, Xero
Replaces steps
1, 2, 3, 4, 5, 7, 10
Estimated build
28 hours, Moderate complexity
// Input
hubspot.deal.id          : string   // unique deal record ID
hubspot.deal.stage       : string   // must equal 'closedwon'
hubspot.contact.email    : string   // primary contact email address
hubspot.contact.firstname: string
hubspot.contact.lastname : string
hubspot.deal.amount      : number   // deal value in USD for Xero invoice
hubspot.deal.owner_email : string   // assigned account manager email
hubspot.company.name     : string
hubspot.deal.service_tier: string   // e.g. 'Starter', 'Growth', 'Enterprise'

// Output (all steps complete)
gmail.welcome_email.sent        : boolean
typeform.intake_link.dispatched : boolean
typeform.intake_form.submitted  : boolean
hubspot.contact.updated         : boolean  // intake fields mapped
hubspot.company.updated         : boolean
xero.invoice.id                 : string   // created invoice reference
xero.invoice.sent               : boolean
hubspot.deal.stage              : string   // updated to 'onboarded'
hubspot.deal.note               : string   // timestamped completion note

// On approval (draft-mode invoices only)
// If xero_draft_mode = true, invoice remains in Xero draft state
// and requires manual approval before dispatch.
// Set xero_draft_mode flag in automation platform config.
  • HubSpot tier: The automation requires HubSpot Operations Hub or Sales Hub Pro or above. The 'deal stage changed' webhook is not available on the free tier. Confirm the active subscription before build starts.
  • Gmail authentication: Use OAuth 2.0 with the gmail.send scope only. Do not request broader mail scopes. The sending address must match the account manager's connected Gmail account or a shared onboarding alias.
  • Typeform intake form: The form must be finalised and field names locked before the mapping step is built. If the form changes post-launch, every Typeform-to-HubSpot field mapping in the automation platform must be updated manually. Confirm field names with the process owner at kick-off.
  • Reminder logic: The 48-hour reminder is implemented as a wait step followed by a conditional branch checking for typeform.submission.received. If the submission is not received, the Gmail reminder fires. The loop checks again at 24 hours and again at 72 hours before logging a fallback alert.
  • Xero contact matching: The invoice creation step queries Xero for an existing contact matching the billing email from the intake form. If no match is found, a new Xero contact is created using intake data. If the billing email is blank, the step must halt and log an error rather than creating a mis-attributed invoice.
  • Xero draft mode: A configuration flag (xero_draft_mode) must be settable per environment. In sandbox it defaults to true. Confirm with the process owner whether production invoices should send automatically or require approval.
  • HubSpot field mapping: All Typeform response fields must be mapped to specific HubSpot contact or company property API names (not display labels). The mapping table must be agreed and documented in the Integration and API Spec before this agent goes into build.
  • Deduplication: Before creating a new HubSpot contact or Xero contact, the agent must check for an existing record by email. Duplicate creation must be blocked and the existing record updated instead.
  • Error logging: Any step failure (Gmail send, Typeform dispatch, Xero invoice creation, HubSpot update) must write a structured error row to the error log table and trigger a Slack alert to the ops channel.
Internal Handoff Agent

The Internal Handoff Agent fires once the Typeform intake form submission is confirmed and the HubSpot record has been updated with intake data. It composes a structured Slack message and posts it to the designated onboarding channel, giving the delivery team the client name, service tier, assigned account manager, and the kickoff scheduling link in a single formatted notification. This agent is intentionally narrow in scope: it reads from HubSpot and writes to Slack, nothing else. It replaces the manual Slack notification step that is frequently delayed or skipped.

Trigger
Typeform intake form submission received AND HubSpot contact record updated with intake data (upstream confirmation from Onboarding Coordinator Agent)
Tools
Slack, HubSpot
Replaces steps
8
Estimated build
6 hours, Simple complexity
// Input
hubspot.contact.firstname    : string
hubspot.contact.lastname     : string
hubspot.company.name         : string
hubspot.deal.service_tier    : string
hubspot.deal.owner_email     : string   // account manager
hubspot.deal.kickoff_link    : string   // Calendly or calendar URL stored on deal
hubspot.deal.id              : string

// Output
slack.message.posted         : boolean
slack.channel                : string   // e.g. '#client-onboarding'
slack.message.ts             : string   // Slack message timestamp for audit

// On approval
// No approval gate on this agent.
// If slack.message.posted = false, error row is written and
// a fallback email is sent to the account manager via Gmail.
  • Slack channel routing: The target channel ('#client-onboarding' or equivalent) must be confirmed with the process owner before build. The channel name should be stored as a configuration variable, not hardcoded in the workflow.
  • Slack bot token: Use a dedicated Slack app bot token with the chat:write scope. Do not reuse a personal user token. The bot must be invited to the target channel before testing.
  • Kickoff link field: The HubSpot deal property for the kickoff scheduling link (e.g. 'hs_kickoff_link' or a custom property) must exist and be populated before this agent fires. If the field is blank, the Slack message should still post but include a placeholder noting the link is pending.
  • Fallback on Slack failure: If the Slack API call fails (rate limit, channel not found, invalid token), write an error row to the log table and send a direct email to the account manager's Gmail address as a fallback. Do not silently drop the notification.
  • Message formatting: Use Slack Block Kit for the notification layout. Include a header block with the client name, a section block with tier and account manager, and a button block linking to the kickoff URL.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with field names, agent handoff points, and branching logic
// ============================================================
// CLIENT ONBOARDING AUTOMATION  --  END-TO-END DATA FLOW
// ============================================================

// TRIGGER
// HubSpot fires webhook on deal.stage == 'closedwon'
INPUT {
  hubspot.deal.id              // e.g. '12345678'
  hubspot.deal.stage           // 'closedwon'
  hubspot.contact.email        // 'client@acme.com'
  hubspot.contact.firstname    // 'Jane'
  hubspot.contact.lastname     // 'Doe'
  hubspot.company.name         // 'Acme Corp'
  hubspot.deal.amount          // 4500.00
  hubspot.deal.owner_email     // 'manager@[YourCompany.com]'
  hubspot.deal.service_tier    // 'Growth'
  hubspot.deal.payment_terms   // 'NET14'
}

// ── ONBOARDING COORDINATOR AGENT ────────────────────────────

// STEP 1: Deduplicate HubSpot contact by email
HubSpot.contacts.search(email: hubspot.contact.email)
  -> existing_contact_id OR create new contact

// STEP 2: Send welcome email via Gmail
Gmail.messages.send {
  to:      hubspot.contact.email
  from:    hubspot.deal.owner_email
  subject: 'Welcome to [YourCompany.com], {{hubspot.contact.firstname}}'
  body:    template(contact.firstname, company.name, service_tier, owner_email)
}
  -> gmail.welcome_email.sent = true

// STEP 3: Dispatch Typeform intake link
Typeform.responses.watch(form_id: CONFIG.intake_form_id)
Gmail.messages.send {
  to:      hubspot.contact.email
  body:    template(typeform.intake_url, contact.firstname)
}
  -> typeform.intake_link.dispatched = true
  -> wait_node.start(duration: 48h, check: typeform.submission.received)

// STEP 4: 48-hour reminder branch
IF typeform.submission.received == false AFTER 48h:
  Gmail.messages.send { reminder email to hubspot.contact.email }
  wait_node.start(duration: 24h, check: typeform.submission.received)
  IF still false AFTER 72h total:
    error_log.write { deal_id, step: 'intake_reminder', status: 'unresolved' }
    Slack.alert { channel: CONFIG.ops_alert_channel, message: 'Intake overdue' }
ELSE:
  -> proceed to STEP 5

// STEP 5: Typeform submission received
typeform.submission {
  tf_business_name      // maps to hubspot.company.name
  tf_billing_email      // maps to hubspot.contact.hs_billing_email
  tf_billing_address    // maps to hubspot.company.address
  tf_service_prefs      // maps to hubspot.contact.service_preferences
  tf_payment_terms      // maps to hubspot.deal.payment_terms (override if present)
  tf_primary_phone      // maps to hubspot.contact.phone
  tf_secondary_contact  // maps to hubspot.company.secondary_contact
}

// STEP 6: Map intake data to HubSpot
HubSpot.contacts.update(contact_id: existing_contact_id) {
  hs_billing_email:      tf_billing_email
  phone:                 tf_primary_phone
  service_preferences:   tf_service_prefs
}
HubSpot.companies.update(company_id: linked_company_id) {
  name:               tf_business_name
  address:            tf_billing_address
  secondary_contact:  tf_secondary_contact
}
  -> hubspot.contact.updated = true
  -> hubspot.company.updated = true

// STEP 7: Deduplicate Xero contact, then create invoice
Xero.contacts.search(email: tf_billing_email)
  -> xero_contact_id OR create new Xero contact from intake data
Xero.invoices.create {
  contact_id:    xero_contact_id
  line_items:    [{ description: hubspot.deal.service_tier, amount: hubspot.deal.amount }]
  due_date:      today + parse(payment_terms)  // e.g. NET14 -> +14 days
  reference:     hubspot.deal.id
}
IF CONFIG.xero_draft_mode == false:
  Xero.invoices.send(invoice_id)
  -> xero.invoice.sent = true
ELSE:
  -> xero.invoice.sent = false  // awaiting manual approval
  -> xero.invoice.id logged

// STEP 8: Update HubSpot deal stage
HubSpot.deals.update(deal_id: hubspot.deal.id) {
  dealstage: 'onboarded'
  hs_note:   'Onboarding sequence completed at ' + timestamp()
}
  -> hubspot.deal.stage = 'onboarded'

// ── AGENT HANDOFF: Onboarding Coordinator -> Internal Handoff ──
// Trigger condition: hubspot.contact.updated == true
// Passed fields: contact.firstname, contact.lastname, company.name,
//   deal.service_tier, deal.owner_email, deal.kickoff_link, deal.id

// ── INTERNAL HANDOFF AGENT ───────────────────────────────────

// STEP 9: Read HubSpot deal for Slack payload
HubSpot.deals.get(deal_id: hubspot.deal.id) {
  -> contact.firstname, contact.lastname
  -> company.name
  -> deal.service_tier
  -> deal.owner_email
  -> deal.kickoff_link  // blank triggers placeholder text
}

// STEP 10: Post Block Kit message to Slack
Slack.chat.postMessage {
  channel: CONFIG.slack_onboarding_channel  // e.g. '#client-onboarding'
  blocks: [
    header:  'New client onboarded: {{company.name}}'
    section: 'Tier: {{service_tier}} | AM: {{owner_email}}'
    button:  'Book Kickoff Call' -> deal.kickoff_link
  ]
}
  -> slack.message.posted = true
  -> slack.message.ts logged for audit

// ── END OF AUTOMATED SEQUENCE ────────────────────────────────
// Remaining manual steps (not automated):
//   Step 6 (original): Provision client account in service platform
//   Step 9 (original): Schedule kickoff call
// ============================================================
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform with native HTTP webhook support, a visual node editor, and a shared credential store. One workflow per agent: 'Onboarding Coordinator' and 'Internal Handoff'. Credentials for HubSpot, Gmail (OAuth 2.0), Typeform, Xero (OAuth 2.0), and Slack (Bot Token) are stored centrally in the platform credential vault and referenced by name, never hardcoded in workflow nodes.
Webhook configuration
HubSpot outbound webhook fires on deal property change: dealstage = closedwon. Register the webhook URL from the automation platform in HubSpot Settings > Integrations > Webhooks. Use HMAC-SHA256 signature verification (HubSpot sends X-HubSpot-Signature-v3 header). Typeform webhook fires on new submission for the specific form ID; register under Typeform Connect > Webhooks with the platform's inbound webhook URL. Both webhooks must be tested in sandbox before production go-live.
Templating approach
Welcome email and reminder email bodies are stored as named templates within the automation platform (or as a template file in the project repository). Variable substitution uses double-brace syntax (e.g. {{contact.firstname}}). Slack Block Kit payloads are defined as JSON templates with variable slots for client name, tier, account manager, and kickoff link. All templates must be version-controlled and reviewed by the process owner before go-live.
Error logging
All step-level failures write a structured row to a Supabase table named 'onboarding_errors' with columns: id (uuid), deal_id (text), agent_name (text), step_name (text), error_message (text), raw_payload (jsonb), created_at (timestamptz). A Supabase database webhook or scheduled query triggers a Slack alert to the ops alert channel (CONFIG.ops_alert_channel) when a new error row is inserted. Alert includes deal_id, step_name, and a link to the Supabase row.
Testing approach
All agents are built and validated in sandbox environments first: HubSpot sandbox account with test deals, Typeform test responses via the Typeform preview submission feature, Xero demo company, and a private Slack channel for test notifications. Gmail sends are tested against an internal test address, not a real client. Sandbox credentials are stored as a separate named credential set in the platform vault. Production credentials are not loaded until the QA sign-off milestone.
Estimated total build time
Onboarding Coordinator Agent: 28 hours. Internal Handoff Agent: 6 hours. End-to-end integration testing and error-path validation: 2 hours. Total: 36 hours.
Pre-build blockers: Three items must be resolved before any build node is created. First, the Typeform intake form must be finalised and field names locked, as any post-build change to field names breaks the HubSpot mapping step. Second, the exact HubSpot contact and company property API names (not display labels) must be agreed and recorded in the Integration and API Spec. Third, the process owner must confirm whether Xero invoices should auto-send on creation or remain in draft mode pending approval. These are recorded as open items in the project tracker.
Questions or build issues: Contact the FullSpec team at support@gofullspec.com. Reference the deal ID and agent name in any support request to allow fast log lookup.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Client / Customer Onboarding.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View