FS-DOC-05Technical
Integration and API Spec
Supplier Onboarding Automation
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document defines every integration point, authentication requirement, field mapping, and error-handling rule for the Supplier Onboarding automation. It is written for the FullSpec build team and covers all six connected tools across the three-agent architecture. Each section provides the exact configuration values, credential references, and retry behaviours needed to build and maintain the orchestration layer without hardcoding any secrets or making assumptions about tool defaults.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Orchestration layer
Hosts all three agent workflows, credential store, and retry logic
Internal service account / env vars
N/A
All agents
Google Forms
Supplier intake data collection; submission event triggers Agent 2
OAuth 2.0 (Google identity)
Google Workspace (any tier) or free consumer account
Agent 1, Agent 2
Gmail
Sends intake form link, timed reminder emails, and exception notifications
OAuth 2.0 (Google identity)
Google Workspace Business Starter or free consumer Gmail
Agent 1, Agent 2
DocuSign
Generates and dispatches trading agreement envelope; monitors signing status
OAuth 2.0 (JWT grant)
DocuSign Standard (eSignature API access required)
Agent 2
Xero
Creates supplier contact record with bank details, payment terms, and tax ID
OAuth 2.0 (authorization code flow)
Xero Starter or above (Contacts and Bank Accounts API must be enabled)
Agent 3
HubSpot
Creates and tags supplier contact record; tracks relationship lifecycle
Private App token (Bearer)
HubSpot Free CRM or above (Contacts API)
Agent 3
Slack
Posts compliance routing alerts, finance lead sign-off requests, and activation confirmations
Bot token (xoxb-) via Slack App
Slack Free or above (Incoming Webhooks + Bot scopes)
Agent 2, Agent 3
Before you connect anything: provision sandbox or test credentials for every tool listed above and validate each connection end-to-end before switching to production credentials. Google, DocuSign, Xero, HubSpot, and Slack all offer sandbox or developer environments. No production data should be written during integration testing.
02Per-tool integration detail
Google Forms
Hosts the supplier intake form. The orchestration layer polls the linked Google Sheet responses tab for new rows, or consumes a Google Apps Script webhook push. Form structure and sheet column order must match the field mapping table in Section 03 exactly.
Auth method
OAuth 2.0 using the Google identity platform. Use a dedicated service account with domain-wide delegation disabled. Store the client_id, client_secret, and refresh_token in the credential store (see Section 04).
Required scopes
https://www.googleapis.com/auth/forms.responses.readonly | https://www.googleapis.com/auth/spreadsheets.readonly
Trigger setup
Poll the linked response spreadsheet (Google Sheets API v4) every 5 minutes for new rows where column A (Timestamp) is later than the last-seen watermark stored in the workflow state. Alternatively, a Google Apps Script onFormSubmit trigger can POST a webhook payload to the orchestration layer's inbound webhook URL; validate the shared secret header X-GForms-Secret against the value stored in the credential store.
Required configuration
GOOGLE_FORMS_FORM_ID stored in credential store. GOOGLE_FORMS_SHEET_ID stored in credential store. Column header row must be row 1; data begins row 2. Do not rename or reorder columns after deployment without updating field mappings.
Rate limits
Google Sheets API: 300 read requests/minute per project. At 8 submissions/month with 5-minute polling, request volume is approximately 8,640 reads/month (well under quota). Throttling is not required at current volume.
Constraints
Form must not use File Upload question type for compliance documents (Google Drive permissions complicate retrieval). Compliance document uploads are handled separately via email attachment parsed by Agent 2. Form response editing must be disabled after submission to prevent data mutation post-trigger.
// Output (row object from Sheets API)
{
"timestamp": "2024-05-13T09:14:22Z",
"supplier_legal_name": "Acme Supplies Pty Ltd",
"supplier_abn_tax_id": "51 824 753 556",
"primary_contact_name": "Jane Doe",
"primary_contact_email": "jane.doe@acmesupplies.com",
"primary_contact_phone": "+61 2 9999 0000",
"bank_bsb": "062-000",
"bank_account_number": "12345678",
"bank_account_name": "Acme Supplies Pty Ltd",
"payment_terms_requested": "Net 30",
"supplier_type": "Goods"
}Gmail
Sends all outbound emails: the initial intake form link, timed reminder emails at 48 hours and 96 hours post-invite, and exception notifications when documents are flagged incomplete. Template IDs are stored in the credential store and must not be hardcoded in workflow logic.
Auth method
OAuth 2.0 using the same Google service account as Google Forms. Reuse the stored refresh_token. Sending domain must match the authenticated account.
Required scopes
https://www.googleapis.com/auth/gmail.send | https://www.googleapis.com/auth/gmail.readonly (readonly required to poll for supplier reply threads if monitoring inbound responses)
Webhook / trigger setup
Outbound only for intake and reminder sends. For inbound monitoring (supplier replies), use Gmail API users.messages.list with q filter label:INBOX from:{supplier_email} after:{unix_timestamp}. Poll every 10 minutes. Store the last-checked timestamp per supplier thread in workflow state.
Required configuration
GMAIL_SENDER_ADDRESS in credential store. GMAIL_INTAKE_TEMPLATE_SUBJECT and GMAIL_REMINDER_TEMPLATE_SUBJECT as workflow environment variables. GOOGLE_FORMS_PUBLIC_URL (the intake form link) in credential store. Reminder schedule: first reminder at T+48h, second at T+96h. After two reminders with no response, escalate to operations channel via Slack.
Rate limits
Gmail API: 250 quota units/second per user; sending limit 500 messages/day for Workspace accounts. At 8 suppliers/month with up to 3 emails each, peak is 24 sends/month. No throttling required.
Constraints
Do not use Gmail SMTP relay; use the REST API only to keep OAuth token management consistent. HTML email templates must be stored as base64-encoded strings in the credential store or as referenced template files, not inlined in workflow logic. Unsubscribe headers are not required for transactional operational email but include a plain-text contact address in every template footer.
// Input (intake send payload)
{
"to": "jane.doe@acmesupplies.com",
"subject": "Action required: Supplier onboarding for Acme Supplies",
"body_template": "GMAIL_INTAKE_TEMPLATE_ID",
"placeholders": {
"{{supplier_name}}": "Acme Supplies Pty Ltd",
"{{form_url}}": "https://forms.gle/XXXXXXXXXXXXXXXX",
"{{deadline_date"}}": "2024-05-15"
}
}
// Output
{ "message_id": "18f3a2c9d4e1b7a0", "thread_id": "18f3a2c9d4e1b7a0", "status": "sent" }DocuSign
Sends the trading agreement envelope to the supplier once the intake form is submitted. Monitors envelope status via webhook callback. Envelope template must be pre-configured in the DocuSign account with named merge fields matching intake form output.
Auth method
OAuth 2.0 JWT grant (server-to-server). The orchestration layer acts as the integration user. Store DOCUSIGN_INTEGRATION_KEY, DOCUSIGN_PRIVATE_KEY_BASE64, DOCUSIGN_ACCOUNT_ID, and DOCUSIGN_IMPERSONATION_USER_ID in the credential store. Token expiry is 1 hour; the platform must refresh automatically before each API call.
Required scopes
signature | impersonation
Webhook / trigger setup
Register a Connect webhook in the DocuSign admin console pointing to the orchestration layer's inbound webhook URL (/webhooks/docusign). Events to subscribe: envelope-sent, envelope-completed, envelope-declined, envelope-voided. Validate webhook payload HMAC signature using DOCUSIGN_CONNECT_HMAC_KEY stored in the credential store. On envelope-completed, Agent 2 marks document review as passed and triggers Agent 3.
Required configuration
DOCUSIGN_TEMPLATE_ID (the trading agreement template) stored in credential store. Template must contain merge fields: {{supplier_legal_name}}, {{supplier_abn_tax_id}}, {{primary_contact_name}}, {{primary_contact_email}}, {{payment_terms_requested}}. Signer role in template must be named Supplier. DOCUSIGN_BASE_URL set to https://na3.docusign.net/restapi for production or https://demo.docusign.net/restapi for sandbox.
Rate limits
DocuSign Standard plan: 100 API calls/hour. At 8 envelopes/month, call volume is negligible. No throttling required.
Constraints
DocuSign Standard plan includes 100 sent envelopes/month. At 8 suppliers/month this is well within quota. Voided or declined envelopes must trigger the exception path: post a Slack alert to the finance lead with the supplier name and envelope ID. Do not auto-resend a voided envelope without human review.
// Input (envelope create request, simplified)
{
"templateId": "{{DOCUSIGN_TEMPLATE_ID}}",
"templateRoles": [{
"roleName": "Supplier",
"name": "Jane Doe",
"email": "jane.doe@acmesupplies.com",
"tabs": {
"textTabs": [
{ "tabLabel": "supplier_legal_name", "value": "Acme Supplies Pty Ltd" },
{ "tabLabel": "supplier_abn_tax_id", "value": "51 824 753 556" }
]
}
}],
"status": "sent"
}
// Output (Connect webhook payload, key fields)
{
"envelopeId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"completedDateTime": "2024-05-14T11:32:00Z"
}Integration and API SpecPage 1 of 4
FS-DOC-05Technical
Xero
Creates the supplier contact record in Xero once the finance lead approves compliance. Contact is created with bank account details, payment terms, and tax ID sourced directly from the intake form response. No data is keyed manually.
Auth method
OAuth 2.0 authorization code flow. The FullSpec team completes the initial authorization during setup to obtain the access_token and refresh_token. Store XERO_CLIENT_ID, XERO_CLIENT_SECRET, XERO_REFRESH_TOKEN, and XERO_TENANT_ID in the credential store. Access tokens expire after 30 minutes; refresh_tokens expire after 60 days and must be rotated by re-authorizing.
Required scopes
openid | profile | email | accounting.contacts | accounting.settings.read | offline_access
Webhook / trigger setup
Xero is write-only in this automation. No inbound webhook is required. The Supplier Activation Agent posts to POST /api.xro/2.0/Contacts using the tenant ID in the X-Xero-Tenant-Id header on every request.
Required configuration
XERO_TENANT_ID in credential store. XERO_DEFAULT_PAYMENT_TERMS (e.g. 30 days net) as a workflow environment variable used when the supplier's requested terms are outside policy. XERO_ACCOUNT_CODE_PURCHASES (the chart-of-accounts code for purchases, e.g. 300) as a workflow environment variable. Duplicate check: before creating, call GET /api.xro/2.0/Contacts?where=Name=="{supplier_legal_name}" to detect existing records. If a match is found, post a Slack alert and halt; do not create a duplicate.
Rate limits
Xero API: 60 calls/minute, 5,000 calls/day per connection. At 8 supplier activations/month, usage is negligible. No throttling required.
Constraints
Bank account details (BSB, account number) are written to the Xero contact's bank account object. Xero does not validate bank account numbers against financial institution records; the automation writes what the supplier submitted. The finance lead's approval serves as the human check. If the Xero plan restricts the accounting.contacts scope, the FullSpec team must escalate to the account owner before build begins.
// Input (POST /api.xro/2.0/Contacts payload)
{
"Contacts": [{
"Name": "Acme Supplies Pty Ltd",
"ContactStatus": "ACTIVE",
"IsSupplier": true,
"TaxNumber": "51824753556",
"PaymentTerms": { "Bills": { "Day": 30, "Type": "DAYSAFTERBILLDATE" } },
"ContactPersons": [{
"FirstName": "Jane",
"LastName": "Doe",
"EmailAddress": "jane.doe@acmesupplies.com",
"IncludeInEmails": true
}],
"BankAccountDetails": "062-000 12345678"
}]
}
// Output (key fields from Xero response)
{
"ContactID": "xero-contact-uuid-001",
"Name": "Acme Supplies Pty Ltd",
"ContactStatus": "ACTIVE"
}HubSpot
Creates a matching supplier contact in HubSpot immediately after the Xero record is confirmed. The contact is tagged as an active supplier using a custom property. The Xero Contact ID is stored as a custom property to maintain cross-system traceability.
Auth method
Private App token (Bearer token). Create a dedicated Private App in HubSpot settings and store the token as HUBSPOT_PRIVATE_APP_TOKEN in the credential store. Do not use API keys (deprecated by HubSpot). Token does not expire but must be rotated if compromised.
Required scopes
crm.objects.contacts.read | crm.objects.contacts.write | crm.schemas.contacts.read | crm.schemas.contacts.write (required to create custom properties if not already present)
Webhook / trigger setup
HubSpot is write-only in this automation. No inbound webhook required. POST to https://api.hubapi.com/crm/v3/objects/contacts with Bearer token. Before creating, search for an existing contact by email using POST /crm/v3/objects/contacts/search with filterGroups on email eq supplier_email. If found, update the existing record rather than creating a duplicate.
Required configuration
HUBSPOT_PRIVATE_APP_TOKEN in credential store. Custom contact properties that must exist in HubSpot before deployment: supplier_status (enumeration: active, inactive, under_review), xero_contact_id (single-line text), supplier_type (enumeration: Goods, Services, Both), abn_tax_id (single-line text), payment_terms (single-line text). These must be created manually in HubSpot or via the schema API during setup.
Rate limits
HubSpot API: 100 requests/10 seconds (private app). At 8 supplier creates/month, usage is negligible. No throttling required.
Constraints
HubSpot Free CRM does not restrict Contacts API access for private apps. If the account uses a paid tier with custom object limits, verify that additional custom properties are within the plan limit. The contact lifecycle stage must be set to 'other' or a custom stage; do not default to 'lead' as this will pollute sales pipeline reporting.
// Input (POST /crm/v3/objects/contacts payload)
{
"properties": {
"firstname": "Jane",
"lastname": "Doe",
"email": "jane.doe@acmesupplies.com",
"phone": "+61 2 9999 0000",
"company": "Acme Supplies Pty Ltd",
"lifecyclestage": "other",
"supplier_status": "active",
"xero_contact_id": "xero-contact-uuid-001",
"supplier_type": "Goods",
"abn_tax_id": "51824753556",
"payment_terms": "Net 30"
}
}
// Output (key fields)
{
"id": "hubspot-contact-id-002",
"properties": { "supplier_status": "active", "xero_contact_id": "xero-contact-uuid-001" }
}Slack
Used by Agent 2 (Document Review Agent) to post the compliance routing alert and sign-off request to the finance lead, and by Agent 3 (Supplier Activation Agent) to post the final activation confirmation to the operations channel. Slash commands or interactive message buttons can be used for the finance lead's approval action.
Auth method
Slack Bot token (xoxb-) via a dedicated Slack App created in the FullSpec workspace integration setup. Store SLACK_BOT_TOKEN in the credential store. Additionally, store SLACK_SIGNING_SECRET for validating inbound interactive payloads.
Required scopes
chat:write | chat:write.public | channels:read | incoming-webhook | commands (if using slash command for finance lead approval) | interactive_messages (for approval button payloads)
Webhook / trigger setup
For the finance lead approval step: configure a Slack interactive message with two action buttons (Approve and Request Changes). The button payload POSTs to the orchestration layer's inbound webhook URL (/webhooks/slack/approval). Validate the X-Slack-Signature header using HMAC-SHA256 with SLACK_SIGNING_SECRET before processing. The payload contains the supplier reference, action value, and responding user ID.
Required configuration
SLACK_FINANCE_CHANNEL_ID (the channel or DM ID for the finance lead's compliance alerts) in the credential store. SLACK_OPS_CHANNEL_ID (the operations channel for activation confirmations) in the credential store. SLACK_BOT_TOKEN in the credential store. Channel IDs must be the internal ID (e.g. C01234ABCDE), not the display name, to avoid breakage if channels are renamed.
Rate limits
Slack Web API: Tier 3 methods (chat.postMessage) allow 50+ calls/minute. At 8 suppliers/month with 2-3 Slack messages each, volume is negligible. No throttling required.
Constraints
The Slack App must be invited to both SLACK_FINANCE_CHANNEL_ID and SLACK_OPS_CHANNEL_ID before deployment. Interactive message payloads expire after 3 seconds if not acknowledged with a 200 response; the orchestration layer must respond immediately and process the action asynchronously. If the finance lead does not respond to the approval request within 24 hours, the platform must send a reminder and log the timeout.
// Input (compliance routing message payload to finance lead)
{
"channel": "{{SLACK_FINANCE_CHANNEL_ID}}",
"text": "Supplier compliance review required: Acme Supplies Pty Ltd",
"blocks": [
{ "type": "section", "text": { "type": "mrkdwn", "text": "*Supplier:* Acme Supplies Pty Ltd\n*ABN:* 51 824 753 556\n*Documents received:* Certificate of Insurance, NDA, Tax Form\n*Gaps:* None" } },
{ "type": "actions", "elements": [
{ "type": "button", "text": { "type": "plain_text", "text": "Approve" }, "value": "approve", "action_id": "supplier_approve" },
{ "type": "button", "text": { "type": "plain_text", "text": "Request Changes" }, "value": "request_changes", "action_id": "supplier_reject" }
]}
]
}
// Inbound interactive payload (approval action, key fields)
{
"type": "block_actions",
"actions": [{ "action_id": "supplier_approve", "value": "approve" }],
"user": { "id": "U0FINANCE01", "name": "tom.bassett" }
}Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The tables below define every field handed off between tools at each agent boundary. Source and destination field names are written in exact monospace format as they appear in the API response or request payload. No transformation logic should rename these fields without updating all downstream mappings.
Agent 1 to Agent 2 handoff: Google Forms response row to Document Review Agent context object
Source tool
Source field
Destination tool
Destination field
Google Forms (Sheets row)
`Timestamp`
Workflow state
`submission_timestamp`
Google Forms (Sheets row)
`supplier_legal_name`
Workflow state
`supplier_legal_name`
Google Forms (Sheets row)
`supplier_abn_tax_id`
Workflow state
`supplier_abn_tax_id`
Google Forms (Sheets row)
`primary_contact_name`
Workflow state
`primary_contact_name`
Google Forms (Sheets row)
`primary_contact_email`
Workflow state
`primary_contact_email`
Google Forms (Sheets row)
`primary_contact_phone`
Workflow state
`primary_contact_phone`
Google Forms (Sheets row)
`bank_bsb`
Workflow state
`bank_bsb`
Google Forms (Sheets row)
`bank_account_number`
Workflow state
`bank_account_number`
Google Forms (Sheets row)
`bank_account_name`
Workflow state
`bank_account_name`
Google Forms (Sheets row)
`payment_terms_requested`
Workflow state
`payment_terms_requested`
Google Forms (Sheets row)
`supplier_type`
Workflow state
`supplier_type`
Agent 2 to DocuSign handoff: Workflow state to DocuSign envelope template merge fields
Source tool
Source field
Destination tool
Destination field
Workflow state
`supplier_legal_name`
DocuSign
`templateRoles[0].name` + tab `supplier_legal_name`
Workflow state
`primary_contact_email`
DocuSign
`templateRoles[0].email`
Workflow state
`primary_contact_name`
DocuSign
`templateRoles[0].name`
Workflow state
`supplier_abn_tax_id`
DocuSign
tab `supplier_abn_tax_id`
Workflow state
`payment_terms_requested`
DocuSign
tab `payment_terms_requested`
Agent 2 to Slack handoff: Document review result to compliance routing message
Source tool
Source field
Destination tool
Destination field
Workflow state
`supplier_legal_name`
Slack
message block `text` (supplier name)
Workflow state
`supplier_abn_tax_id`
Slack
message block `text` (ABN display)
Workflow state
`documents_received[]`
Slack
message block `text` (documents list)
Workflow state
`document_gaps[]`
Slack
message block `text` (gaps list; empty string if none)
DocuSign
`envelopeId`
Slack
message block `text` (envelope reference link)
Agent 3 handoff: Workflow state to Xero contact creation
Source tool
Source field
Destination tool
Destination field
Workflow state
`supplier_legal_name`
Xero
`Contacts[0].Name`
Workflow state
`supplier_abn_tax_id`
Xero
`Contacts[0].TaxNumber`
Workflow state
`primary_contact_name`
Xero
`Contacts[0].ContactPersons[0].FirstName` + `LastName`
Workflow state
`primary_contact_email`
Xero
`Contacts[0].ContactPersons[0].EmailAddress`
Workflow state
`payment_terms_requested`
Xero
`Contacts[0].PaymentTerms.Bills.Day` (parsed integer)
Workflow state
`bank_bsb`
Xero
`Contacts[0].BankAccountDetails` (concatenated: BSB + account number)
Workflow state
`bank_account_number`
Xero
`Contacts[0].BankAccountDetails` (concatenated)
Workflow state
`supplier_type`
Xero
`Contacts[0].IsSupplier` = true (always); `AccountCode` resolved from env var
Agent 3 handoff: Workflow state + Xero response to HubSpot contact creation
Source tool
Source field
Destination tool
Destination field
Workflow state
`primary_contact_name`
HubSpot
`properties.firstname` + `properties.lastname`
Workflow state
`primary_contact_email`
HubSpot
`properties.email`
Workflow state
`primary_contact_phone`
HubSpot
`properties.phone`
Workflow state
`supplier_legal_name`
HubSpot
`properties.company`
Workflow state
`supplier_abn_tax_id`
HubSpot
`properties.abn_tax_id`
Workflow state
`payment_terms_requested`
HubSpot
`properties.payment_terms`
Workflow state
`supplier_type`
HubSpot
`properties.supplier_type`
Xero response
`ContactID`
HubSpot
`properties.xero_contact_id`
Hardcoded / constant
"active"
HubSpot
`properties.supplier_status`
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent, running within a single automation platform project. All workflows share a single credential store and a shared workflow state object passed via internal variables or a lightweight data store (key-value). No workflow hardcodes any secret, ID, or URL: all are resolved at runtime from the credential store.
Agent 1 trigger (Intake and Chaser Agent)
Scheduled poll: every 5 minutes, reads new rows from the Google Forms linked Sheets spreadsheet using the last-seen watermark. Alternatively, accepts a webhook push from Google Apps Script onFormSubmit validated by X-GForms-Secret header.
Agent 2 trigger (Document Review Agent)
Webhook: fires when Agent 1 emits a form-submitted event to the internal event bus, or when a Google Forms webhook fires. Additionally triggers on DocuSign Connect webhook (envelope-completed event) validated by DOCUSIGN_CONNECT_HMAC_KEY HMAC-SHA256 signature check.
Agent 3 trigger (Supplier Activation Agent)
Webhook: fires when the orchestration layer receives a Slack interactive message payload with action_id=supplier_approve, validated by X-Slack-Signature HMAC-SHA256 using SLACK_SIGNING_SECRET. Payload must include the supplier reference key to look up workflow state.
Credential store type
Encrypted environment variable store within the automation platform project. No credentials are written to logs, workflow node descriptions, or comment fields. Access is restricted to the FullSpec build team service account.
State persistence
Workflow state for each supplier onboarding run is keyed by a UUID generated at Agent 1 trigger time (supplier_run_id). State is stored in the platform's built-in data store or an equivalent key-value mechanism for the duration of the onboarding lifecycle (maximum 30 days, then auto-purged).
Credential store: all entries are encrypted env vars; never log, comment, or hardcode these values
// Credential store contents (all values stored as encrypted env vars)
// Google / Gmail / Forms
GOOGLE_CLIENT_ID=<oauth2_client_id>
GOOGLE_CLIENT_SECRET=<oauth2_client_secret>
GOOGLE_REFRESH_TOKEN=<oauth2_refresh_token>
GOOGLE_FORMS_FORM_ID=<forms_form_id>
GOOGLE_FORMS_SHEET_ID=<linked_sheet_id>
GOOGLE_FORMS_PUBLIC_URL=https://forms.gle/<form_hash>
GMAIL_SENDER_ADDRESS=onboarding@[YourCompany.com]
GMAIL_INTAKE_TEMPLATE_SUBJECT=Action required: Supplier onboarding
GMAIL_REMINDER_TEMPLATE_SUBJECT=Reminder: Supplier form outstanding
GFORMS_WEBHOOK_SECRET=<shared_secret_for_apps_script_push>
// DocuSign
DOCUSIGN_INTEGRATION_KEY=<docusign_app_integration_key>
DOCUSIGN_PRIVATE_KEY_BASE64=<base64_encoded_rsa_private_key>
DOCUSIGN_ACCOUNT_ID=<docusign_account_uuid>
DOCUSIGN_IMPERSONATION_USER_ID=<docusign_user_uuid>
DOCUSIGN_TEMPLATE_ID=<envelope_template_uuid>
DOCUSIGN_BASE_URL=https://na3.docusign.net/restapi
DOCUSIGN_CONNECT_HMAC_KEY=<connect_webhook_hmac_key>
// Xero
XERO_CLIENT_ID=<xero_app_client_id>
XERO_CLIENT_SECRET=<xero_app_client_secret>
XERO_REFRESH_TOKEN=<xero_oauth2_refresh_token>
XERO_TENANT_ID=<xero_organisation_uuid>
XERO_DEFAULT_PAYMENT_TERMS=30
XERO_ACCOUNT_CODE_PURCHASES=300
// HubSpot
HUBSPOT_PRIVATE_APP_TOKEN=pat-na1-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// Slack
SLACK_BOT_TOKEN=xoxb-xxxxxxxxxxxx-xxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx
SLACK_SIGNING_SECRET=<slack_app_signing_secret>
SLACK_FINANCE_CHANNEL_ID=C01FINANCE01
SLACK_OPS_CHANNEL_ID=C01OPSXXX01
The XERO_REFRESH_TOKEN expires after 60 days of inactivity. The FullSpec team must implement an automatic refresh rotation: on each successful token refresh, write the new refresh_token back to the credential store immediately. If rotation fails, alert support@gofullspec.com and halt Xero writes until the token is renewed.
Integration and API SpecPage 3 of 4
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 written to the platform error log and, where marked, must also trigger a Slack alert to the operations channel or a notification to support@gofullspec.com.
Integration
Scenario
Required behaviour
Google Forms / Sheets poll
Sheets API returns 429 Too Many Requests
Pause polling for 60 seconds and retry with exponential backoff (60s, 120s, 240s). After 3 failed retries, alert SLACK_OPS_CHANNEL_ID and halt the poll cycle. Resume on next scheduled interval.
Google Forms / Sheets poll
Duplicate row detected (same supplier_legal_name + primary_contact_email submitted twice within 24 hours)
Deduplicate using supplier_run_id watermark. Skip the second row, log the skip event with both row timestamps, and post a warning to SLACK_OPS_CHANNEL_ID. Do not create duplicate records downstream.
Gmail send (intake or reminder)
Gmail API returns 500 or 503
Retry up to 3 times with exponential backoff (30s, 60s, 120s). If all retries fail, log the failure with the supplier_run_id and post an alert to SLACK_OPS_CHANNEL_ID with the supplier name and intended recipient. Do not silently drop the send.
Gmail send (intake)
Supplier email address is malformed or bounces (4xx or 5xx SMTP error returned via API)
Mark the supplier run as blocked. Post a Slack alert to SLACK_OPS_CHANNEL_ID with the invalid address and supplier name. Halt further automation for this run until a corrected email is supplied by the operations team.
DocuSign envelope creation
Template merge field is missing from the workflow state (e.g. supplier_abn_tax_id is null)
Abort envelope creation. Log the missing field name and supplier_run_id. Post an alert to SLACK_OPS_CHANNEL_ID. Do not send an incomplete envelope. Require manual resolution before retrying.
DocuSign Connect webhook
Inbound webhook HMAC signature validation fails
Return HTTP 401 immediately. Log the failed validation attempt with the raw X-Signature header value and source IP. Do not process the payload. Alert the FullSpec team at support@gofullspec.com if more than 3 failures occur within 5 minutes (potential replay attack).
DocuSign envelope
Envelope status = declined or voided
Post a Slack alert to SLACK_FINANCE_CHANNEL_ID with the supplier name and envelope ID. Mark the supplier run as requires_review. Do not auto-resend. Log the event. Await manual instruction before creating a new envelope.
Xero contact creation
Duplicate supplier detected (existing contact with matching Name found via GET /Contacts search)
Halt contact creation. Post a Slack alert to SLACK_OPS_CHANNEL_ID with the existing ContactID and the incoming supplier_legal_name. Log the conflict. Do not overwrite the existing record. Await manual resolution.
Xero contact creation
Xero API returns 401 Unauthorized (token expired or refresh failed)
Attempt one token refresh using XERO_REFRESH_TOKEN. If refresh succeeds, retry the API call once. If refresh fails, halt all Xero writes for this run, log the token failure, and alert support@gofullspec.com. Do not continue the activation flow without a valid token.
HubSpot contact creation
HubSpot API returns 409 Conflict (contact with email already exists)
Switch to an update operation: PATCH /crm/v3/objects/contacts/{id} with the new property values. Log that an update was performed instead of a create. Post a Slack notification to SLACK_OPS_CHANNEL_ID noting the existing HubSpot contact was updated.
Slack interactive message (approval)
Finance lead does not respond within 24 hours
Re-post the approval request message to SLACK_FINANCE_CHANNEL_ID with a [REMINDER] prefix. Log the timeout event. If no response after a further 24 hours (48 hours total), escalate by posting to SLACK_OPS_CHANNEL_ID and marking the run as escalated. Do not auto-approve.
Slack interactive message (approval)
Inbound Slack payload X-Slack-Signature validation fails
Return HTTP 400 immediately. Log the failure with timestamp and user_id if present. Do not process the action. Alert the FullSpec team at support@gofullspec.com if more than 2 validation failures occur within 10 minutes.
All error events must be written to the platform's persistent error log with at minimum: timestamp, supplier_run_id, agent name, integration name, error code or message, and retry count. Errors marked red in the table above are blocking: the supplier onboarding run must not proceed past the failed step until the error is resolved or manually overridden by the FullSpec team. Contact support@gofullspec.com for any blocking error that persists beyond the retry window.
Integration and API SpecPage 4 of 4