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
Contractor & Freelancer Management
[YourCompany.com] · HR Department · Prepared by FullSpec · [Today's Date]
This document is the primary technical reference for the FullSpec team building the Contractor and Freelancer Management automation. It covers the current-state process map, both agent specifications, the end-to-end data flow, and the recommended build stack. All implementation decisions made during the discovery and mapping phase are recorded here so the build can proceed without ambiguity. Where a detail requires confirmation before build can start, it is explicitly flagged.
01Process snapshot
02Agent specifications
Handles the full onboarding sequence from the moment a contractor is confirmed in HubSpot. The agent creates the contractor contact record, dispatches the correct DocuSign agreement based on role and rate, sends a structured document collection request with a Google Drive upload link, and monitors completion status on a schedule. If required documents have not been received within three business days, the agent sends a follow-up reminder to the contractor and posts an alert to the configured HR Slack channel. The agent repeats reminders on a configurable cadence and only escalates to the HR team when a document is flagged as unreadable, incorrect, or persistently missing beyond the escalation threshold. Once DocuSign is fully countersigned and all compliance documents are confirmed in the target Drive folder, the agent triggers payee creation in Gusto and marks the contractor record in HubSpot as onboarding complete. The agent does not auto-approve any document it cannot parse; it flags the record and holds the Gusto step until a human clears the exception.
// Input
hubspot.contact.id : string
hubspot.contact.email : string
hubspot.contact.role : string // e.g. 'Graphic Designer'
hubspot.contact.rate_usd : number // agreed hourly or project rate
hubspot.contact.start_date : ISO8601
hubspot.contact.tag : 'contractor'
// Output
hubspot.contact.stage : 'onboarding_complete'
docusign.envelope.status : 'completed'
docusign.envelope.id : string
google_drive.folder_id : string // contractor-specific subfolder
google_drive.files[] : { name, mime_type, upload_timestamp }
gusto.contractor.id : string
gusto.contractor.status : 'active'
slack.alert.channel : '#hr-contractor-ops'
slack.alert.text : string // posted only on exception or escalation
// On exception (missing or unreadable document)
hubspot.contact.flag : 'document_exception'
slack.alert.text : 'ACTION REQUIRED: [contractor_name] document check failed'
gusto.step : HELD // payee creation paused until cleared- HubSpot tier must support workflow triggers via API (Operations Hub Starter or above, or equivalent API access plan). Confirm tier before build starts.
- DocuSign templates must be created, role-mapped (Contractor signer, Company countersigner), and activated in the DocuSign account before the agent can select and pre-fill them. Template IDs are passed as environment variables; the agent must not hard-code them.
- Google Drive folder structure must be agreed and pre-created (e.g. /Contractors/{contractor_id}/Compliance and /Contractors/{contractor_id}/Agreements) before the document-filing step is wired. The agent uses the contractor's HubSpot contact ID as the folder name to ensure uniqueness.
- Reminder cadence defaults to Day 3 and Day 6 after initial document request. A third reminder at Day 9 triggers Slack escalation to #hr-contractor-ops. Cadence values must be stored as environment variables, not hard-coded.
- Gusto payee creation requires the Gusto Embedded Payroll or Plus API tier. Confirm the active Gusto plan includes contractor API write access before committing this step to the build.
- Dedupe logic: the agent checks for an existing HubSpot contact with a matching email address before creating a new record. If a duplicate is found, the agent updates the existing record and posts a Slack alert to the HR channel rather than creating a second contact.
- DocuSign completion webhook must be registered in the DocuSign Connect configuration to push envelope status events to the automation platform listener endpoint. Polling is not acceptable for this step.
- Fallback for undeliverable document collection email: if the contractor email bounces, the agent flags the HubSpot record and posts a Slack alert immediately rather than continuing the onboarding sequence.
Receives incoming contractor invoices submitted either by email to a monitored inbox or through a linked intake form, then validates the invoice amount against the agreed rate stored in HubSpot for the matching contractor record. If the amount matches within the configured tolerance (default: zero variance, or within 2% if a tolerance environment variable is set), the agent routes a Slack approval message to the assigned manager, including the invoice amount, contractor name, engagement period, and an approve or query action button. The manager's response is captured and written back to the HubSpot deal or activity record. On approval, the agent creates a bill in Xero with the correct expense category, attaches the invoice PDF, schedules payment for the next payment run, and sends a confirmation email to the contractor. If the invoice amount does not match the agreed rate, the agent flags the record in HubSpot, holds all payment steps, and posts a detailed mismatch alert to the #finance-ops Slack channel for manual review by the Finance Administrator. The agent never auto-approves a mismatched invoice.
// Input invoice.contractor_email : string invoice.amount_usd : number invoice.invoice_number : string invoice.invoice_date : ISO8601 invoice.period_start : ISO8601 invoice.period_end : ISO8601 invoice.pdf_attachment : base64 | URL hubspot.contact.rate_usd : number // pulled by matching contractor_email hubspot.contact.id : string // Output (matched invoice, approved) xero.bill.id : string xero.bill.status : 'APPROVED' xero.bill.account_code : string // e.g. '400' Contractor Expenses xero.payment.scheduled_date : ISO8601 gusto.payment.reference : string // if Gusto payment run triggered hubspot.activity.type : 'invoice_approved' hubspot.activity.timestamp : ISO8601 email.contractor.subject : 'Your invoice [invoice_number] has been approved' slack.approval.response : 'approved' | 'queried' // On approval (Slack interaction payload) slack.action.value : 'approve' | 'query' slack.action.user : string // manager Slack user ID slack.action.timestamp : ISO8601 // On mismatch (rate does not match) hubspot.contact.flag : 'invoice_mismatch' slack.alert.channel : '#finance-ops' slack.alert.text : 'MISMATCH: [contractor_name] invoiced $[amount] vs agreed $[rate]' xero.step : HELD gusto.step : HELD
- Contractor rate data must be stored in a dedicated HubSpot contact property (suggested property name: contractor_agreed_rate_usd) and populated at the point of engagement. If rates are held in a spreadsheet and not migrated to HubSpot before build, the matching logic cannot function.
- Invoice parsing: the agent extracts invoice_number, amount, period, and contractor email from the PDF using a structured parsing step. If parsing confidence is below threshold (suggested: 90%), the agent flags the invoice for manual review rather than proceeding with potentially incorrect data.
- Slack approval message must include a direct link to the invoice PDF and display the HubSpot contractor record URL. The Slack app must be installed in the workspace with bot token scopes: chat:write, incoming-webhook, and interactive messages (block kit actions).
- Xero account code for contractor expenses must be agreed with the Finance Administrator before build. Store as an environment variable (XERO_CONTRACTOR_ACCOUNT_CODE). Default suggestion: account code 400 (Contractor Services).
- Gusto payment trigger is optional per engagement; some contractors may be paid via Xero only. Add a HubSpot contact property (payment_method: 'xero' | 'gusto') so the agent routes payment creation correctly per contractor.
- Manager approval timeout: if no Slack response is received within 24 hours, the agent sends a reminder to the same manager. After 48 hours with no response, a secondary alert is posted to #finance-ops for escalation. Timeout values stored as environment variables.
- Dedupe logic: the agent checks invoice_number against a logged Xero bill reference before processing. If a bill with the same reference already exists, the agent skips creation and posts a duplicate warning to #finance-ops.
- Xero API uses OAuth 2.0 with a 30-minute access token and a 60-day refresh token. The orchestration layer must handle token refresh silently. Confirm that the Xero organisation is connected under the correct tenant ID before build.
03End-to-end data flow
// ─────────────────────────────────────────────────────────────────
// TRIGGER: New contractor engagement confirmed
// ─────────────────────────────────────────────────────────────────
TRIGGER_SOURCE : 'hubspot_contact_created' | 'form_submission'
-> payload.contact_email : string
-> payload.contact_name : string
-> payload.role : string
-> payload.rate_usd : number
-> payload.start_date : ISO8601
-> payload.tag : 'contractor'
// ──────────────────────────���──────────────────────────────────────
// AGENT 1 HANDOFF: Contractor Onboarding Agent receives trigger
// ─────────────────────────────────────────────────────────────────
// Step A: HubSpot record creation / deduplication
HubSpot.contacts.search(email: payload.contact_email)
IF existing_contact FOUND
-> HubSpot.contacts.update(id, { tag: 'contractor', stage: 'onboarding' })
-> Slack.post(channel: '#hr-contractor-ops', text: 'Duplicate contact updated: [name]')
ELSE
-> HubSpot.contacts.create({
email, firstname, lastname, role,
contractor_agreed_rate_usd: rate_usd,
start_date, lifecyclestage: 'contractor',
hs_lead_status: 'onboarding'
})
-> hubspot.contact.id : string // stored for all downstream steps
// Step B: DocuSign agreement dispatch
DocuSign.envelopes.create({
templateId: ENV.DOCUSIGN_TEMPLATE_ID_FOR_ROLE,
signers: [{ email, name, role: 'Contractor' }],
prefill: { rate_usd, start_date, role }
})
-> docusign.envelope.id : string
-> docusign.envelope.status : 'sent'
-> HubSpot.activity.log({ type: 'docusign_sent', envelope_id })
// Step C: Document collection request
Email.send({
to: payload.contact_email,
template: 'contractor_onboarding_checklist',
vars: {
contractor_name, required_docs: ['W-9','NDA','Bank Details','Certification'],
upload_link: google_drive.upload_url(folder: '/Contractors/{contact_id}/Compliance')
}
})
-> email.sent_timestamp : ISO8601
// Step D: Document chase loop
SCHEDULE.check(after: '3 business days', condition: google_drive.folder_complete == false)
IF incomplete
-> Email.send(to: contact_email, template: 'doc_reminder_1')
SCHEDULE.check(after: '6 business days', condition: google_drive.folder_complete == false)
-> Email.send(to: contact_email, template: 'doc_reminder_2')
SCHEDULE.check(after: '9 business days', condition: google_drive.folder_complete == false)
-> Slack.post(channel: '#hr-contractor-ops',
text: 'ESCALATION: [name] documents still incomplete after 9 days')
-> HubSpot.contacts.update(id, { hs_lead_status: 'document_exception' })
// Step E: DocuSign completion webhook received
WEBHOOK: DocuSign.Connect -> platform_listener_url
-> docusign.envelope.status : 'completed'
-> Google_Drive.files.upload({
folder: '/Contractors/{contact_id}/Agreements',
file: docusign.signed_pdf,
name: '{contact_id}_agreement_signed_{timestamp}.pdf'
})
-> HubSpot.contacts.update(id, { docusign_status: 'completed' })
// Step F: Gusto payee creation (fires when Drive folder complete AND DocuSign complete)
CONDITION: google_drive.folder_complete == true AND docusign.status == 'completed'
-> Gusto.contractors.create({
first_name, last_name, email,
wage: rate_usd,
wage_type: 'Fixed' // or 'Hourly' per ENV.GUSTO_WAGE_TYPE
})
-> gusto.contractor.id : string
-> HubSpot.contacts.update(id, {
gusto_contractor_id: gusto.contractor.id,
hs_lead_status: 'onboarding_complete'
})
// ─────────────────────────────────────────────────────────────────
// AGENT 2 HANDOFF: Invoice Processing Agent receives invoice
// ─────────────────────────────────────────────────────────────────
// Step G: Invoice intake and parsing
TRIGGER_SOURCE : 'email_inbox_monitor' | 'invoice_form_submission'
-> invoice.from_email : string
-> invoice.pdf_attachment : base64 | URL
-> invoice.invoice_number : string // parsed from PDF
-> invoice.amount_usd : number // parsed from PDF
-> invoice.period_start : ISO8601 // parsed from PDF
-> invoice.period_end : ISO8601 // parsed from PDF
// Step H: Contractor record lookup and rate match
HubSpot.contacts.search(email: invoice.from_email)
-> hubspot.contact.id : string
-> hubspot.contractor_agreed_rate_usd : number
CONDITION: abs(invoice.amount_usd - hubspot.rate_usd) <= ENV.RATE_TOLERANCE
IF mismatch
-> HubSpot.contacts.update(id, { flag: 'invoice_mismatch' })
-> Slack.post(channel: '#finance-ops',
text: 'MISMATCH: [name] invoiced $[amount] vs agreed $[rate]')
-> HALT // all Xero and Gusto steps held
// Step I: Slack approval routing (match confirmed)
Slack.message.send({
channel: hubspot.contact.assigned_manager_slack_id,
blocks: [
{ type: 'section', text: 'Invoice from [name]: $[amount] for [period]' },
{ type: 'actions', elements: [
{ action_id: 'approve_invoice', text: 'Approve' },
{ action_id: 'query_invoice', text: 'Query' }
]}
]
})
-> slack.message.ts : string // stored for interaction callback
WEBHOOK: Slack.InteractivePayload -> platform_listener_url
-> slack.action.value : 'approve' | 'query'
-> slack.action.user.id : string
-> slack.action.timestamp : ISO8601
-> HubSpot.activity.log({ type: 'invoice_approval_decision', value, user, ts })
// Step J: Xero bill creation and payment scheduling (on approve)
CONDITION: slack.action.value == 'approve'
-> Xero.invoices.create({
type: 'ACCPAY',
contact: { email: invoice.from_email },
lineItems: [{
description: 'Contractor services [period_start] to [period_end]',
quantity: 1,
unitAmount: invoice.amount_usd,
accountCode: ENV.XERO_CONTRACTOR_ACCOUNT_CODE
}],
reference: invoice.invoice_number,
attachments: [{ filename, content_base64: invoice.pdf_attachment }]
})
-> xero.bill.id : string
-> xero.bill.status : 'APPROVED'
-> Xero.payments.create({
invoice_id: xero.bill.id,
date: next_payment_run_date,
account_id: ENV.XERO_BANK_ACCOUNT_ID
})
-> xero.payment.scheduled_date : ISO8601
// Step K: Gusto payment trigger (if contractor.payment_method == 'gusto')
CONDITION: hubspot.contact.payment_method == 'gusto'
-> Gusto.contractor_payments.create({
contractor_id: hubspot.gusto_contractor_id,
amount: invoice.amount_usd,
date: xero.payment.scheduled_date
})
-> gusto.payment.reference : string
// Step L: Contractor confirmation email
Email.send({
to: invoice.from_email,
template: 'invoice_approved_confirmation',
vars: {
invoice_number: invoice.invoice_number,
amount_usd: invoice.amount_usd,
payment_date: xero.payment.scheduled_date
}
})
// ─────────────────────────────────────────────────────────────────
// END OF FLOW
// ─────────────────────────────────────────────────────────────────04Recommended build stack
More documents for this process
Every document generated for Contractor & Freelancer Management.