Back to Invoice Generation & Delivery

Integration and API Spec

The reference during the build: every tool connection, auth scope, field mapping, and error-handling rule.

4 pagesPDF · Technical
FS-DOC-05Technical

Integration and API Spec

Invoice Generation and Delivery

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

This document provides the complete integration and API specification for the Invoice Generation and Delivery automation. It covers every tool in the stack, the exact authentication methods and OAuth scopes required, field-level mappings between systems, orchestration layout, credential store contents, and defined error handling behaviour for every integration point. The FullSpec team uses this document to build, configure, and validate all connections. Your team does not need to action anything in this document directly, but the finance lead and operations manager should review sections 03 and 04 to confirm field mappings and credential ownership before the build begins.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
HubSpot
Deal trigger source and CRM sync target
OAuth 2.0 (private app token)
Starter CRM (API access enabled)
Agent 1, Agent 2
Xero
Invoice creation, draft storage, and approval event source
OAuth 2.0 (PKCE flow)
Xero Starter or above
Agent 1, Agent 2
Gmail
Branded invoice delivery to client billing contact
OAuth 2.0 (Google Workspace)
Google Workspace (any paid tier)
Agent 2
Google Sheets
Invoice tracking log and payment terms mapping table
OAuth 2.0 (Google Workspace, same grant as Gmail)
Google Workspace (any paid tier)
Agent 1, Agent 2
Slack
Finance team notifications for draft ready and invoice sent events
OAuth 2.0 (Slack app bot token)
Free or paid Slack workspace
Agent 2
Orchestration layer
Workflow automation tool connecting all tools; houses credential store and retry logic
Platform-managed (credentials stored per-connection)
Plan supporting webhook ingestion and multi-step branching
Agent 1, Agent 2
Before you connect anything: sandbox-test every integration using non-production credentials before switching to live API keys or tokens. For HubSpot and Xero, use their respective sandbox or demo environments. For Gmail and Google Sheets, use a test Google Workspace account. For Slack, use a dedicated #automation-testing channel. No production data or live client records should be touched during connection testing.

02Per-tool integration detail

HubSpot

HubSpot is the trigger source for Agent 1 (Invoice Builder Agent) and the CRM write target for Agent 2 (Delivery and CRM Sync Agent). It provides deal properties, company contact data, and receives the invoice reference and updated deal stage on completion.

Auth method
OAuth 2.0 via HubSpot Private App. Generate a private app token in Settings > Integrations > Private Apps. Store the token in the credential store as HUBSPOT_PRIVATE_APP_TOKEN. Do not use legacy API keys.
Required scopes
crm.objects.deals.read, crm.objects.deals.write, crm.objects.contacts.read, crm.objects.companies.read, crm.objects.line_items.read, crm.objects.line_items.write, timeline.write
Webhook / trigger setup
Configure a CRM workflow in HubSpot that fires when deal property 'dealstage' equals the Closed-Won or Complete stage ID. The workflow sends an outbound HTTP POST to the orchestration layer's inbound webhook URL. Alternatively, the orchestration layer may poll the Deals endpoint (GET /crm/v3/objects/deals) every 5 minutes filtered by 'dealstage' and 'hs_lastmodifieddate'. Webhook is preferred. Validate webhook payloads using the X-HubSpot-Signature-v3 header with HUBSPOT_CLIENT_SECRET.
Required configuration
Store the Closed-Won stage ID and Complete stage ID in the credential store as HUBSPOT_STAGE_CLOSED_WON and HUBSPOT_STAGE_COMPLETE. Store the Invoiced stage ID as HUBSPOT_STAGE_INVOICED. Store the invoice reference custom property internal name as HUBSPOT_INVOICE_REF_FIELD (default: 'xero_invoice_reference'). Do not hardcode stage IDs.
Rate limits
Private app tokens: 110 requests per 10 seconds per token (burst), 40,000 per day. At 90 invoices/month (approximately 3/day), no throttling is needed. The orchestration layer should respect a 200 ms inter-request delay as a precaution.
Constraints
Line items must be associated to the deal in HubSpot before the trigger fires; deals with zero associated line items must be routed to an error branch. Company records must have a populated billing email field or the agent must fall back to the primary contact email.
// Trigger payload (inbound webhook or poll result)
deal.id             -> string  (HubSpot deal object ID)
deal.dealname       -> string  (invoice description fallback)
deal.amount         -> number  (total deal value)
deal.dealstage      -> string  (must match HUBSPOT_STAGE_CLOSED_WON or HUBSPOT_STAGE_COMPLETE)
deal.closedate      -> ISO8601 date
// Fetched separately
deal.line_items[]   -> array   (name, quantity, unit_price, hs_sku)
company.name        -> string
company.billing_email -> string
company.payment_terms -> string  (custom property: 'payment_terms_tier')
company.tax_classification -> string  (custom property: 'tax_classification')
// Written back by Agent 2
deal.dealstage      <- HUBSPOT_STAGE_INVOICED
deal.xero_invoice_reference <- string  (Xero InvoiceNumber)
Xero

Xero is the invoice creation target for Agent 1 and the approval event source for Agent 2. Agent 1 creates a draft invoice via the Xero Invoices API. Agent 2 listens for the invoice status to change from DRAFT to AUTHORISED (approved) before triggering downstream delivery steps.

Auth method
OAuth 2.0 with PKCE flow. Register a web app in Xero Developer Portal. Access and refresh tokens stored in credential store as XERO_ACCESS_TOKEN and XERO_REFRESH_TOKEN. Refresh tokens expire after 60 days of inactivity; the orchestration layer must refresh proactively before expiry using the token endpoint (https://identity.xero.com/connect/token).
Required scopes
openid, profile, email, accounting.transactions, accounting.transactions.read, accounting.contacts, accounting.contacts.read, accounting.settings.read, offline_access
Webhook / trigger setup
Register a webhook in the Xero Developer Portal for the Invoice event type, filtering on status changes. The delivery URL is the orchestration layer's Xero inbound webhook endpoint. Xero signs payloads with HMAC-SHA256 using the webhook key; validate every payload against the 'x-xero-signature' header using XERO_WEBHOOK_KEY stored in the credential store. Only process events where invoiceStatus equals 'AUTHORISED' and invoiceType equals 'ACCREC'.
Required configuration
Store the active Xero tenant (organisation) ID in the credential store as XERO_TENANT_ID. Store the Xero BrandingThemeID for invoice templates as XERO_BRANDING_THEME_ID. Store the default AccountCode for sales line items as XERO_DEFAULT_ACCOUNT_CODE (e.g. '200'). Payment terms and tax type mappings are read from the Google Sheets mapping table at runtime; tax type codes (e.g. 'TAX001', 'EXEMPTOUTPUT') must not be hardcoded.
Rate limits
Xero API: 60 requests per minute (concurrent limit: 60 calls/60s rolling window), 5,000 requests per day per tenant. At 90 invoices/month (approximately 3/day, each requiring 2-3 API calls), throughput is well within limits. No throttling required at current volume. Monitor daily call count via the 'X-Rate-Limit-Remaining' response header and log a warning if it drops below 500.
Constraints
Xero contacts must exist before invoice creation; the agent must look up the contact by email using GET /contacts?emailAddress= and create the contact if not found. Invoices in DRAFT status cannot be emailed via the Xero API directly; delivery is handled via Gmail by Agent 2 after AUTHORISED status is confirmed. Xero PDF export endpoint (GET /invoices/{invoiceId}/pdf) is used to retrieve the invoice PDF for Gmail attachment.
// Agent 1 writes (POST /invoices)
Type                 = 'ACCREC'
Contact.ContactID    -> looked up by billing email
LineItems[].Description  -> deal line item name
LineItems[].Quantity     -> deal line item quantity
LineItems[].UnitAmount   -> deal line item unit_price
LineItems[].AccountCode  -> XERO_DEFAULT_ACCOUNT_CODE
LineItems[].TaxType      -> resolved from payment_terms mapping table
CurrencyCode         -> deal currency (default: 'USD')
DueDateString        -> calculated from closedate + payment_terms days
Status               = 'DRAFT'
BrandingThemeID      -> XERO_BRANDING_THEME_ID
// Agent 2 reads (webhook event)
InvoiceID            -> string  (used to fetch PDF and retrieve InvoiceNumber)
InvoiceNumber        -> string  (written back to HubSpot)
Contact.EmailAddress -> string  (confirmed billing address for Gmail send)
Gmail

Gmail is used by Agent 2 to send the approved invoice to the client billing contact. The email uses a branded HTML template with the invoice PDF attached. The send is triggered only after Xero webhook confirmation of AUTHORISED status.

Auth method
OAuth 2.0 via Google Workspace. A dedicated service account or a delegated user credential (finance-automation@[yourdomain]) is used. Store the refresh token as GMAIL_REFRESH_TOKEN and the sending address as GMAIL_SEND_FROM. Do not use personal Gmail accounts.
Required scopes
https://www.googleapis.com/auth/gmail.send, https://www.googleapis.com/auth/gmail.compose
Webhook / trigger setup
No inbound webhook. Gmail is a write-only integration in this automation. The orchestration layer calls the Gmail API (POST /gmail/v1/users/me/messages/send) after assembling the multipart MIME message with the HTML body and PDF attachment.
Required configuration
Store the HTML email template as GMAIL_INVOICE_TEMPLATE_HTML in the credential store or as a referenced file path. Template must contain placeholders: {{client_name}}, {{invoice_number}}, {{invoice_amount}}, {{due_date}}, {{payment_link}}. Store the reply-to address as GMAIL_REPLY_TO. Store the BCC address for the finance team as GMAIL_BCC_FINANCE. Do not hardcode any of these values.
Rate limits
Gmail API: 250 quota units per user per second; sending a message costs 100 units. At 90 invoices/month, peak load is negligible. Daily sending quota for Google Workspace accounts is 2,000 messages/day per user. No throttling required.
Constraints
The invoice PDF must be retrieved from Xero (GET /invoices/{invoiceId}/pdf) and base64-encoded before attaching. Maximum attachment size via Gmail API is 25 MB; Xero PDFs are typically under 500 KB. If the PDF fetch fails, the send step must not proceed and must trigger the error branch.
// Input to Gmail send step
to                   -> Contact.EmailAddress (from Xero webhook event)
cc                   -> (empty unless deal property 'cc_contact_email' is populated)
bcc                  -> GMAIL_BCC_FINANCE
reply_to             -> GMAIL_REPLY_TO
subject              -> 'Invoice {{invoice_number}} from [YourCompany.com]'
body_html            -> GMAIL_INVOICE_TEMPLATE_HTML (rendered with deal values)
attachment.filename  -> 'Invoice_{{invoice_number}}.pdf'
attachment.mimeType  -> 'application/pdf'
attachment.data      -> base64(Xero PDF bytes)
// Output
messageId            -> string  (logged to Google Sheets row for audit trail)
Google Sheets

Google Sheets serves two roles: it hosts the payment terms and tax code mapping table read by Agent 1 at runtime, and it receives an appended tracking row from Agent 2 after each invoice is sent. Both operations target the same Google Sheets file but different named sheets (tabs).

Auth method
OAuth 2.0 via Google Workspace, shared credential grant with Gmail. Store the refresh token as GSHEETS_REFRESH_TOKEN (may be the same token as Gmail if scopes are combined in one OAuth grant). Store the Spreadsheet ID as GSHEETS_SPREADSHEET_ID.
Required scopes
https://www.googleapis.com/auth/spreadsheets
Webhook / trigger setup
No webhook. Agent 1 reads the mapping table on each run using GET /v4/spreadsheets/{spreadsheetId}/values/PaymentTermsMapping!A:D. Agent 2 appends a row using POST /v4/spreadsheets/{spreadsheetId}/values/InvoiceLog!A:J:append.
Required configuration
Store GSHEETS_SPREADSHEET_ID in the credential store. The PaymentTermsMapping tab must have columns: [client_type, payment_terms_label, payment_terms_days, xero_tax_type]. The InvoiceLog tab must have columns: [invoice_number, client_name, client_email, amount, currency, send_date, due_date, deal_id, xero_invoice_id, gmail_message_id]. Sheet names and column order must not be changed without updating the field mapping configuration.
Rate limits
Google Sheets API: 300 requests per minute per project, 60 requests per minute per user. At 90 invoices/month, approximately 2 reads and 1 write per invoice (270 calls/month total), well within limits. No throttling required.
Constraints
The PaymentTermsMapping tab is the single source of truth for tax type and payment days logic. If a client_type value from HubSpot does not match any row in the mapping table, Agent 1 must route to an error branch and alert via Slack rather than defaulting to any hardcoded value.
// Agent 1 reads (PaymentTermsMapping tab)
client_type          -> matched against company.tax_classification from HubSpot
payment_terms_days   -> integer (used to calculate Xero DueDateString)
xero_tax_type        -> string (e.g. 'TAX001') passed to Xero LineItems[].TaxType
// Agent 2 appends (InvoiceLog tab)
invoice_number       <- Xero InvoiceNumber
client_name          <- Xero Contact.Name
client_email         <- Xero Contact.EmailAddress
amount               <- Xero Total
currency             <- Xero CurrencyCode
send_date            <- ISO8601 timestamp of Gmail send
due_date             <- Xero DueDateString
deal_id              <- HubSpot deal.id
xero_invoice_id      <- Xero InvoiceID
gmail_message_id     <- Gmail messageId
Slack

Slack receives two types of notification from Agent 2: a draft-ready alert sent to the finance lead when Agent 1 creates the Xero draft, and a send-confirmation alert posted to the finance channel after Gmail delivery completes.

Auth method
OAuth 2.0 via Slack app bot token. Create a Slack app in api.slack.com/apps, install to workspace, and store the bot token as SLACK_BOT_TOKEN in the credential store.
Required scopes
chat:write, chat:write.public, users:read.email (to resolve finance lead Slack user ID by email if using DM for draft-ready alert)
Webhook / trigger setup
No inbound webhook. Slack is a write-only integration. Use the Slack Web API (POST /api/chat.postMessage) rather than legacy incoming webhooks to allow richer block kit message formatting. Store the finance channel ID as SLACK_FINANCE_CHANNEL_ID and the finance lead's Slack user ID as SLACK_FINANCE_LEAD_USER_ID.
Required configuration
Store SLACK_BOT_TOKEN, SLACK_FINANCE_CHANNEL_ID, and SLACK_FINANCE_LEAD_USER_ID in the credential store. Draft-ready DMs include the Xero deep link (https://go.xero.com/AccountsReceivable/Edit.aspx?invoiceId={{xero_invoice_id}}). Send-confirmation channel messages include client name, invoice number, amount, and the same Xero deep link. Message templates must not contain hardcoded channel names.
Rate limits
Slack Web API: Tier 3 rate limit for chat.postMessage is 50 requests per minute. At 90 invoices/month and 2 messages per invoice (180 messages/month), this is well within limits. No throttling required.
Constraints
If Slack delivery fails, the failure must be logged but must not block or roll back upstream steps (Gmail send, HubSpot update, Sheets append). Slack is a notification layer only. Bot token must be rotated if the Slack app is uninstalled and reinstalled.
// Draft-ready DM to finance lead (sent by Agent 1 after Xero draft created)
channel              -> SLACK_FINANCE_LEAD_USER_ID
text                 -> 'Draft invoice {{invoice_number}} for {{client_name}} ({{amount}} {{currency}}) is ready for review.'
blocks[].accessory  -> Button: 'Review in Xero' -> xero_deep_link
// Send-confirmation channel message (sent by Agent 2 after Gmail send)
channel              -> SLACK_FINANCE_CHANNEL_ID
text                 -> 'Invoice {{invoice_number}} sent to {{client_email}} for {{amount}} {{currency}}. Due: {{due_date}}.'
blocks[].accessory  -> Button: 'View in Xero' -> xero_deep_link
Integration and API SpecPage 1 of 3
FS-DOC-05Technical

03Field mappings between tools

The tables below define every field handoff between tools at each agent boundary. Use exact field names as shown. Any field name change in a source system must be reflected here and in the orchestration layer configuration before deployment.

Agent 1 handoff: HubSpot deal record and Google Sheets mapping table to Xero draft invoice.

Source tool
Source field
Destination tool
Destination field
HubSpot
deal.id
Xero
Reference (deal ID stored for traceability)
HubSpot
deal.dealname
Xero
LineItems[0].Description (fallback if no line items)
HubSpot
deal.amount
Xero
SubTotal (cross-check only; line items are authoritative)
HubSpot
deal.closedate
Xero
Date (invoice date, ISO8601)
HubSpot
company.name
Xero
Contact.Name (matched or created)
HubSpot
company.billing_email
Xero
Contact.EmailAddress
HubSpot
line_item.name
Xero
LineItems[n].Description
HubSpot
line_item.quantity
Xero
LineItems[n].Quantity
HubSpot
line_item.price
Xero
LineItems[n].UnitAmount
HubSpot
line_item.hs_sku
Xero
LineItems[n].ItemCode
HubSpot
company.tax_classification
Google Sheets
PaymentTermsMapping lookup key: client_type
Google Sheets
payment_terms_days
Xero
DueDateString (closedate + payment_terms_days)
Google Sheets
xero_tax_type
Xero
LineItems[n].TaxType
Credential store
XERO_DEFAULT_ACCOUNT_CODE
Xero
LineItems[n].AccountCode
Credential store
XERO_BRANDING_THEME_ID
Xero
BrandingThemeID

Agent 2 handoff: Xero approved invoice event to Gmail, HubSpot, Google Sheets, and Slack.

Source tool
Source field
Destination tool
Destination field
Xero (webhook)
InvoiceID
Gmail
attachment fetch key (GET /invoices/{InvoiceID}/pdf)
Xero (webhook)
InvoiceNumber
Gmail
subject: 'Invoice {{InvoiceNumber}} from [YourCompany.com]'
Xero (webhook)
InvoiceNumber
HubSpot
deal.xero_invoice_reference (custom property)
Xero (webhook)
InvoiceNumber
Google Sheets
InvoiceLog!invoice_number
Xero (webhook)
Contact.Name
Gmail
body_html: {{client_name}} placeholder
Xero (webhook)
Contact.Name
Google Sheets
InvoiceLog!client_name
Xero (webhook)
Contact.EmailAddress
Gmail
to (recipient address)
Xero (webhook)
Contact.EmailAddress
Google Sheets
InvoiceLog!client_email
Xero (webhook)
Total
Gmail
body_html: {{invoice_amount}} placeholder
Xero (webhook)
Total
Google Sheets
InvoiceLog!amount
Xero (webhook)
CurrencyCode
Google Sheets
InvoiceLog!currency
Xero (webhook)
DueDateString
Gmail
body_html: {{due_date}} placeholder
Xero (webhook)
DueDateString
Google Sheets
InvoiceLog!due_date
Xero (webhook)
InvoiceID
Slack
xero_deep_link construction
HubSpot (context)
deal.id
Google Sheets
InvoiceLog!deal_id
HubSpot (context)
deal.id
HubSpot
deal stage update target (HUBSPOT_STAGE_INVOICED)
Gmail (response)
messageId
Google Sheets
InvoiceLog!gmail_message_id
Orchestration layer
send_timestamp
Google Sheets
InvoiceLog!send_date (ISO8601)

04Build stack and orchestration

Orchestration layout
Two discrete workflows are deployed: one per agent. Workflow 1 (Invoice Builder Agent) is triggered by HubSpot deal stage change and terminates after the Xero draft is created and the Slack draft-ready DM is sent. Workflow 2 (Delivery and CRM Sync Agent) is triggered by the Xero AUTHORISED webhook event and terminates after the Slack send-confirmation message is posted. Both workflows share a single credential store. No logic is shared across workflows at runtime; each is independently testable and re-runnable.
Agent 1 trigger mechanism
Webhook (preferred): HubSpot CRM workflow posts to Workflow 1's inbound webhook URL when deal stage changes to HUBSPOT_STAGE_CLOSED_WON or HUBSPOT_STAGE_COMPLETE. Payload signature validated using X-HubSpot-Signature-v3 and HUBSPOT_CLIENT_SECRET. Fallback: poll HubSpot Deals endpoint every 5 minutes, filtering by dealstage and hs_lastmodifieddate greater than last successful run timestamp stored in state.
Agent 1 signature validation
Compute HMAC-SHA256 of (client_secret + http_method + url + request_body + timestamp). Compare against X-HubSpot-Signature-v3. Reject and log any request where the computed hash does not match or the timestamp is older than 300 seconds.
Agent 2 trigger mechanism
Webhook: Xero posts to Workflow 2's inbound webhook URL on invoice status change. Payload signature validated using HMAC-SHA256 of the raw request body keyed against XERO_WEBHOOK_KEY, compared against the x-xero-signature header. Only events where invoiceStatus equals 'AUTHORISED' and invoiceType equals 'ACCREC' proceed past the filter step. All other events return HTTP 200 and are discarded.
Agent 2 signature validation
Compute HMAC-SHA256 of raw request body bytes using XERO_WEBHOOK_KEY. Base64-encode the result and compare against x-xero-signature header value. Return HTTP 200 on all requests (including invalid ones) to prevent Xero from disabling the webhook endpoint after repeated non-200 responses.
State management
Agent 1 stores last-polled timestamp (for fallback poll mode) in the orchestration layer's built-in key-value store keyed as 'hubspot_last_poll_ts'. No other persistent state is required between runs. Each workflow execution is stateless beyond this timestamp.
Execution environment
Both workflows run on the chosen automation platform's cloud-hosted runtime. No self-hosted infrastructure is required at current volume (90 invoices/month). Execution logs are retained for 30 days minimum and are accessible to the FullSpec team via the platform's built-in log viewer.
Credential store structure
All secrets and configuration values are stored in the automation platform's encrypted credential or environment variable store. See code block below for the full list of required entries. No credentials appear in workflow node configuration fields or code expressions directly.
Credential store: required entries (all keys, no values shown)
# HubSpot
HUBSPOT_PRIVATE_APP_TOKEN      # Private app bearer token
HUBSPOT_CLIENT_SECRET          # Used for webhook signature validation
HUBSPOT_STAGE_CLOSED_WON       # Deal stage ID for Closed-Won
HUBSPOT_STAGE_COMPLETE         # Deal stage ID for Complete
HUBSPOT_STAGE_INVOICED         # Deal stage ID written back after send
HUBSPOT_INVOICE_REF_FIELD      # Internal name of invoice reference custom property

# Xero
XERO_ACCESS_TOKEN              # OAuth 2.0 access token (short-lived, auto-refreshed)
XERO_REFRESH_TOKEN             # OAuth 2.0 refresh token (60-day expiry)
XERO_TENANT_ID                 # Xero organisation (tenant) ID
XERO_BRANDING_THEME_ID         # Invoice template/branding theme ID
XERO_DEFAULT_ACCOUNT_CODE      # Default sales account code (e.g. '200')
XERO_WEBHOOK_KEY               # Webhook signing key for payload validation

# Gmail
GMAIL_REFRESH_TOKEN            # OAuth 2.0 refresh token for sending account
GMAIL_SEND_FROM                # Sending address (e.g. finance-automation@yourdomain.com)
GMAIL_REPLY_TO                 # Reply-to address for client responses
GMAIL_BCC_FINANCE              # BCC address for finance team archive
GMAIL_INVOICE_TEMPLATE_HTML    # Path or content of HTML email template

# Google Sheets
GSHEETS_REFRESH_TOKEN          # OAuth 2.0 refresh token (may share Gmail grant)
GSHEETS_SPREADSHEET_ID         # Spreadsheet ID for mapping table and invoice log

# Slack
SLACK_BOT_TOKEN                # Bot OAuth token (xoxb-...)
SLACK_FINANCE_CHANNEL_ID       # Channel ID for send-confirmation messages
SLACK_FINANCE_LEAD_USER_ID     # Slack user ID for draft-ready DMs
All credential store entries must be populated before either workflow is activated. The FullSpec team will verify each entry during the pre-launch credential check. Rotating any token or secret requires updating the credential store entry and re-testing the affected integration. Contact support@gofullspec.com immediately if a credential is suspected to be compromised.
Integration and API SpecPage 2 of 3
FS-DOC-05Technical

05Error handling and retry logic

Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. All errors must be logged with the workflow execution ID, the failing step name, the error code or message, and the input payload that caused the failure. Where a retry is defined, exponential backoff is applied unless stated otherwise.

Integration
Scenario
Required behaviour
HubSpot webhook inbound
Signature validation fails (mismatched hash or stale timestamp)
Return HTTP 200. Log the rejection with raw headers and body. Do not process the payload. Alert to SLACK_FINANCE_CHANNEL_ID if more than 3 validation failures occur within 10 minutes.
HubSpot: fetch deal line items
Deal has zero associated line items
Route to error branch. Do not create Xero draft. Post DM to SLACK_FINANCE_LEAD_USER_ID: 'Invoice creation skipped for deal {deal.id}: no line items found. Manual action required.' Log and halt.
HubSpot: fetch contact / company
Billing email field is empty on company record
Fall back to primary contact email from associated contact record. If that is also empty, route to error branch, post Slack alert, and halt. Do not proceed to Xero draft creation without a valid email address.
Google Sheets: payment terms lookup
client_type value from HubSpot does not match any row in PaymentTermsMapping tab
Route to error branch. Do not create Xero draft. Post DM to SLACK_FINANCE_LEAD_USER_ID: 'Unknown client type "{client_type}" for deal {deal.id}. Update mapping table and re-trigger.' Log and halt. Never default to a hardcoded tax type.
Google Sheets: mapping table read
API returns 4xx or 5xx, or spreadsheet is inaccessible
Retry 3 times with exponential backoff: 30 s, 60 s, 120 s. After 3 failures, post Slack alert and halt the workflow execution. Log the HTTP status code and response body.
Xero: create draft invoice
API returns 400 (validation error, e.g. invalid tax type or account code)
Do not retry. Log the full Xero error response body. Post DM to SLACK_FINANCE_LEAD_USER_ID with error detail and deal ID. Halt. Manual correction of the mapping table or deal data is required before re-triggering.
Xero: create draft invoice
API returns 429 (rate limit) or 503 (service unavailable)
Retry up to 3 times with exponential backoff: 60 s, 120 s, 240 s. After 3 failures, post Slack alert and log. The deal remains in Closed-Won stage and can be manually re-triggered once Xero is available.
Xero: OAuth token refresh
Refresh token is expired or revoked (refresh token older than 60 days without use)
Halt all Xero-dependent workflow executions immediately. Log the error and post urgent alert to SLACK_FINANCE_CHANNEL_ID and email support@gofullspec.com. Re-authorisation via the OAuth PKCE flow is required. This is a manual recovery path.
Xero webhook inbound (Agent 2)
Payload signature invalid or invoiceStatus is not 'AUTHORISED'
Return HTTP 200 to Xero in all cases (to prevent webhook deactivation). Discard the event silently if invoiceStatus is not 'AUTHORISED'. Log and alert only if signature validation fails more than 5 times in one hour.
Xero: fetch invoice PDF
PDF endpoint returns 404 or times out
Retry once after 30 s. If the second attempt fails, do not send the Gmail. Post DM to SLACK_FINANCE_LEAD_USER_ID: 'Could not retrieve PDF for invoice {InvoiceNumber}. Gmail send skipped. Manual send required.' Log and halt Agent 2 execution. HubSpot and Sheets updates must also be skipped to prevent a partial state.
Gmail: send invoice
API returns 4xx (e.g. invalid recipient address) or message rejected
Do not retry. Log the Gmail API error code and response. Post DM to SLACK_FINANCE_LEAD_USER_ID with the recipient address and error detail. Halt. Manual send from Xero or Gmail is required. HubSpot deal stage must not be updated to Invoiced until send is confirmed.
Gmail: send invoice
API returns 500 or 503 (transient Google-side error)
Retry up to 3 times with exponential backoff: 30 s, 60 s, 120 s. If all retries fail, post Slack alert and halt. Log the execution ID so the send can be manually re-triggered from the last successful checkpoint (Xero AUTHORISED event).
HubSpot: write back deal stage and invoice reference
API returns 4xx or 5xx after Gmail send succeeds
Retry 3 times with 30 s backoff. If retries exhausted, log the failure and post Slack alert with deal ID and invoice number so the finance team can update HubSpot manually. The Gmail send is already complete and must not be repeated. Google Sheets append should still proceed.
Google Sheets: append invoice log row
API returns error or append fails after Gmail send
Retry 3 times with 30 s backoff. If retries exhausted, log the full row payload to the orchestration layer's execution log so it can be manually added to the sheet. Post Slack alert. Do not block or roll back the Gmail send or HubSpot update.
Slack: post notification (any)
API returns error or Slack is unavailable
Retry once after 15 s. If the retry fails, log the failure and continue. Slack is a notification layer only. A Slack failure must never block or roll back any upstream step (Gmail, HubSpot, or Sheets). Log the intended message content so it can be posted manually if needed.
Partial state is the highest-risk failure mode in this automation. If Gmail sends successfully but HubSpot or Sheets updates fail, the invoice has left the business but the internal record does not reflect it. All retry and alert logic is designed to surface these partial failures immediately so the finance team can resolve them before they affect cash flow tracking or client follow-up. Every workflow execution ID is logged so FullSpec can diagnose and replay any failed step.
Integration and API SpecPage 3 of 3

More documents for this process

Every document generated for Invoice Generation & Delivery.

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