Developer Handover Pack
Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.
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
02Agent specifications
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.
// 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.
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.
// 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.
03End-to-end data flow
// ============================================================
// 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
// ============================================================04Recommended build stack
More documents for this process
Every document generated for Client / Customer Onboarding.