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
Supplier Onboarding Automation
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for the Supplier Onboarding automation build. It covers the current-state step map, all three agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The FullSpec team uses this document to guide every build and integration decision. It assumes familiarity with REST APIs, webhook configuration, and OAuth 2.0 authentication flows.
01Process snapshot
02Agent specifications
Handles all initial supplier outreach and timed follow-up without any manual involvement. When a new approved supplier record is detected in the connected source (Google Form trigger, CRM entry, or spreadsheet row), this agent sends a personalised intake form link via Gmail and monitors for submission. If no submission is detected after 48 hours, a first reminder is dispatched automatically. A second reminder fires at 96 hours if the form remains incomplete. The agent's sole output is a confirmed, fully completed intake form submission that downstream agents can act on.
// Input supplier_name: string supplier_email: string approval_date: ISO8601 date approved_by: string // Output form_submission_id: string // Google Forms response ID supplier_business_name: string supplier_tax_id: string // ABN or tax reference supplier_bank_bsb: string supplier_bank_account: string supplier_primary_contact_name: string supplier_primary_contact_email: string supplier_primary_contact_phone: string submission_timestamp: ISO8601 datetime reminder_count: integer // 0, 1, or 2
- Gmail API requires OAuth 2.0 with scopes gmail.send and gmail.readonly. The sending Gmail account must be a verified sender in the connected Google Workspace.
- Google Forms responses must be written to a connected Google Sheet (Forms native response sheet). The automation platform polls this sheet on a schedule of no more than 5-minute intervals or uses a webhook-compatible intermediary layer.
- Intake form template must be finalised and locked before build begins. Any field-name change in the form after build requires a corresponding update to the field-mapping in the agent.
- Deduplication: before sending the intake email, the agent must check whether a submission already exists for the supplier email address in the current response sheet. If a prior submission is found and it is less than 7 days old, no email is sent and the existing submission is forwarded downstream.
- Fallback behaviour: if the Gmail send call returns a non-2xx HTTP response, the agent must log the error to the error table and post a Slack alert to the operations channel with the supplier name, email, and error code.
- Reminder timing is anchored to the initial send timestamp stored in the workflow run context, not to a calendar cron. Both reminders must use a distinct email subject line to avoid threading with the original.
- Confirm before build: the supplier approval trigger source must be agreed and connected (Google Sheet, HubSpot deal stage, or other). This determines the polling mechanism used in this agent.
Fires when the intake form submission is confirmed and documents are attached or linked. This agent checks submitted documents against a configured required-items checklist (insurance certificate, signed NDA or trading terms, tax form). It detects missing items and checks expiry dates on date-stamped documents where parseable. When all required items are present and valid, the agent posts a structured Slack notification to the finance lead summarising the supplier, listing each document with its status, and providing a direct approval or rejection action link. When items are missing or expired, the agent routes a gap report to the finance lead and triggers a re-request email to the supplier via Gmail. The agent does not assess the legal sufficiency of document content.
// Input
form_submission_id: string
supplier_name: string
supplier_email: string
supplier_tax_id: string
documents_received: array<{doc_type: string, file_url: string, expiry_date: date|null}>
// Output (all-docs-complete path)
document_review_status: 'complete' | 'incomplete'
completeness_report: array<{doc_type: string, status: 'present'|'missing'|'expired', expiry_date: date|null}>
slack_notification_ts: string // Slack message timestamp for threading
docusign_envelope_id: string // Created and sent on complete path
docusign_envelope_status: string // 'sent'
// Output (gap-detected path)
document_review_status: 'incomplete'
gap_report: array<{doc_type: string, reason: 'missing'|'expired'}>
supplier_gap_email_sent: boolean
finance_alert_sent: boolean
// On approval (received from Supplier Activation Agent trigger)
docusign_envelope_signed_timestamp: ISO8601 datetime
docusign_completed_envelope_id: string- DocuSign integration requires an API key and a connected DocuSign account on at minimum the Standard plan (Personal plan does not support API access). Confirm the DocuSign plan tier before build.
- The required-document checklist is stored as a configurable list in the workflow. It must be reviewed and agreed with the process owner before build. Changes to the checklist after build require a workflow update.
- Document completeness check is a presence check and, where metadata is available, an expiry date comparison against the current date. The agent cannot read document content for legal accuracy. That responsibility stays with the finance lead.
- DocuSign envelope template must be pre-built in the DocuSign account with the correct signer role and field placements. The agent populates the envelope template with supplier name, email, and tax ID from the intake form output. Template ID must be stored as a workflow credential variable.
- The Slack notification to the finance lead must use a Slack Block Kit message with clearly labelled approve and flag actions tied to a Slack interactive component or a direct URL to an approval step in the automation platform.
- Fallback: if DocuSign envelope creation fails, the agent logs the error and sends a Slack alert to the operations channel. The DocuSign send is not retried automatically; manual intervention is flagged.
- Confirm before build: agree the Slack channel ID and the finance lead's Slack user ID for direct mentions in the notification message.
Fires when the finance lead confirms compliance approval via the Slack interactive action or the connected approval step. This agent creates the supplier record in Xero (including bank account, payment terms, and tax information sourced directly from the intake form output), creates a matching contact record in HubSpot tagged as an active supplier, and posts a final Slack confirmation to the operations channel listing the supplier name, Xero contact ID, and activation date. This agent is the terminal step of the automated flow and its outputs are the source of truth for downstream procurement and accounts-payable processes.
// Input supplier_name: string supplier_email: string supplier_tax_id: string supplier_bank_bsb: string supplier_bank_account: string supplier_primary_contact_name: string supplier_primary_contact_phone: string docusign_completed_envelope_id: string approval_confirmed_by: string // Slack user ID of finance lead approval_timestamp: ISO8601 datetime // Output xero_contact_id: string // Created Xero contact record ID hubspot_contact_id: string // Created HubSpot contact record ID slack_activation_message_ts: string // Posted to operations channel activation_date: ISO8601 date
- Xero API requires OAuth 2.0 with the accounting.contacts scope. The connected Xero account must have the Adviser or Standard user role to create supplier contacts. Confirm the Xero subscription plan: some lower-tier plans restrict API-created contact fields such as bank account details.
- Before creating a Xero contact, the agent must query the Xero Contacts endpoint for an existing contact matching the supplier tax ID or email. If a match is found, the agent must not create a duplicate; instead it logs the conflict and sends a Slack alert to the operations channel for manual resolution.
- HubSpot contact creation requires an API key or private app token with crm.objects.contacts.write scope. The contact must be tagged with the custom property supplier_status set to 'active' and the lifecycle stage set to 'other'.
- Before creating a HubSpot contact, the agent must search by email using the HubSpot Search API to prevent duplicate records. If an existing contact is found, the agent updates the record rather than creating a new one.
- The Slack confirmation message to the operations channel must include: supplier display name, Xero contact ID, HubSpot contact ID, activation date, and a link to the Xero contact record. Use Slack Block Kit for consistent formatting.
- Payment terms value written to Xero must be sourced from a configurable workflow variable (e.g. NET30) and confirmed with the process owner before build. It is not derived from the intake form.
- Confirm before build: the Xero tenant ID (for multi-org Xero accounts), the target HubSpot portal ID, and the Slack operations channel ID.
03End-to-end data flow
// ─────────────────────────────────────────────────────────────────
// TRIGGER: New approved supplier record detected
// Source: Google Sheet row | HubSpot deal stage | spreadsheet entry
// ─────────────────────────────────────────────────────────────────
trigger.supplier_name -> workflow.context.supplier_name
trigger.supplier_email -> workflow.context.supplier_email
trigger.approval_date -> workflow.context.approval_date
trigger.approved_by -> workflow.context.approved_by
// ─────────────────────────────────────────────────────────────────
// AGENT 1: Intake and Chaser Agent
// ─────────────────────────────────────────────────────────────────
// Deduplication check
Google_Forms.response_sheet.lookup(supplier_email)
-> IF existing_submission_age < 7_days: SKIP send, forward existing
-> ELSE: proceed
// Initial outreach
Gmail.send({
to: workflow.context.supplier_email,
subject: 'Action required: Complete your supplier onboarding form',
body: template_intake_email(supplier_name, form_url),
})
-> gmail.message_id -> workflow.context.initial_email_id
-> send_timestamp -> workflow.context.intake_email_sent_at
// Reminder logic (48h + 96h from intake_email_sent_at)
IF Google_Forms.response_sheet.poll() == no_submission AND elapsed >= 48h:
Gmail.send(template_reminder_1) -> workflow.context.reminder_1_sent_at
IF Google_Forms.response_sheet.poll() == no_submission AND elapsed >= 96h:
Gmail.send(template_reminder_2) -> workflow.context.reminder_2_sent_at
workflow.context.reminder_count = 2
// Form submission detected
Google_Forms.response_sheet.on_new_row:
form_submission_id -> workflow.context.form_submission_id
supplier_business_name -> workflow.context.supplier_business_name
supplier_tax_id -> workflow.context.supplier_tax_id
supplier_bank_bsb -> workflow.context.supplier_bank_bsb
supplier_bank_account -> workflow.context.supplier_bank_account
supplier_primary_contact_name -> workflow.context.supplier_primary_contact_name
supplier_primary_contact_email -> workflow.context.supplier_primary_contact_email
supplier_primary_contact_phone -> workflow.context.supplier_primary_contact_phone
submission_timestamp -> workflow.context.submission_timestamp
// ── HANDOFF: Intake and Chaser Agent -> Document Review Agent ────
// Payload: form_submission_id, supplier_name, supplier_email,
// supplier_tax_id, submission_timestamp
// ─────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────
// AGENT 2: Document Review Agent
// ─────────────────────────────────────────────────────────────────
// Document completeness check
checklist = ['insurance_certificate', 'nda_or_trading_terms', 'tax_form']
documents_received[] -> check_each_against(checklist)
doc.doc_type -> match to checklist item
doc.expiry_date -> compare to today() IF not null
-> completeness_report[] -> {doc_type, status, expiry_date}
// Branch: all docs complete and valid
IF document_review_status == 'complete':
// Send DocuSign trading agreement
DocuSign.Envelopes.create({
templateId: env.DOCUSIGN_TEMPLATE_ID,
signerEmail: workflow.context.supplier_email,
signerName: workflow.context.supplier_primary_contact_name,
fields: {
supplier_name: workflow.context.supplier_business_name,
supplier_tax_id: workflow.context.supplier_tax_id,
}
})
-> docusign_envelope_id -> workflow.context.docusign_envelope_id
-> envelope_status -> 'sent'
// Notify finance lead via Slack
Slack.chat.postMessage({
channel: env.SLACK_FINANCE_LEAD_USER_ID,
blocks: template_compliance_review_block(
supplier_name, completeness_report, docusign_envelope_id
),
actions: ['approve', 'flag_gaps']
})
-> slack_notification_ts -> workflow.context.slack_notification_ts
// Branch: gaps detected
IF document_review_status == 'incomplete':
Gmail.send(template_gap_request_email(supplier_email, gap_report))
Slack.chat.postMessage(template_gap_alert(finance_lead, gap_report))
-> finance_alert_sent = true
// DocuSign signed event received (webhook callback)
DocuSign.Connect.webhook.on_envelope_completed:
docusign_completed_envelope_id -> workflow.context.docusign_completed_envelope_id
signed_timestamp -> workflow.context.docusign_signed_at
// ── HANDOFF: Document Review Agent -> Supplier Activation Agent ──
// Trigger: Slack approval action OR DocuSign envelope completed
// Payload: supplier_name, supplier_email, supplier_tax_id,
// supplier_bank_bsb, supplier_bank_account,
// supplier_primary_contact_name, supplier_primary_contact_phone,
// docusign_completed_envelope_id, approval_confirmed_by,
// approval_timestamp
// ─────────────────────────────────────────────────────────────────
// ────────────────────────────────────────────────────────────��────
// AGENT 3: Supplier Activation Agent
// ─────────────────────────────────────────────────────────────────
// Xero deduplication
Xero.Contacts.get({ where: 'TaxNumber=="' + supplier_tax_id + '"' })
-> IF match found: log conflict, Slack alert, HALT
-> ELSE: proceed
// Create Xero supplier contact
Xero.Contacts.create({
Name: workflow.context.supplier_business_name,
EmailAddress: workflow.context.supplier_email,
TaxNumber: workflow.context.supplier_tax_id,
BankAccountNumber: workflow.context.supplier_bank_account,
BSBNumber: workflow.context.supplier_bank_bsb,
PaymentTerms: env.DEFAULT_PAYMENT_TERMS,
IsSupplier: true
})
-> xero_contact_id -> workflow.context.xero_contact_id
// HubSpot deduplication
HubSpot.CRM.Contacts.search({ filterGroups: [{email: supplier_email}] })
-> IF match found: update record, skip create
-> ELSE: proceed
// Create HubSpot contact
HubSpot.CRM.Contacts.create({
email: workflow.context.supplier_email,
firstname: supplier_primary_contact_name.split(' ')[0],
lastname: supplier_primary_contact_name.split(' ')[1],
phone: workflow.context.supplier_primary_contact_phone,
company: workflow.context.supplier_business_name,
supplier_status: 'active',
lifecyclestage: 'other'
})
-> hubspot_contact_id -> workflow.context.hubspot_contact_id
// Post activation confirmation to Slack
Slack.chat.postMessage({
channel: env.SLACK_OPS_CHANNEL_ID,
blocks: template_activation_block(
supplier_name, xero_contact_id, hubspot_contact_id,
activation_date, xero_record_url
)
})
-> slack_activation_message_ts -> workflow.context.slack_activation_message_ts
// ─────────────────────────────────────────────────────────────────
// TERMINAL: Supplier is active in Xero, HubSpot, and Slack confirmed
// ─────────────────────────────────────────────────────────────────04Recommended build stack
More documents for this process
Every document generated for Supplier Onboarding.