FS-DOC-05Technical
Integration and API Spec
Accounts Payable Management
[YourCompany.com] · Finance Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every integration point in the Accounts Payable Management automation. It covers tool authentication, exact API scopes, webhook configuration, field mappings between agents, orchestration layout, credential storage, and failure behaviour. The FullSpec team uses this specification to configure and test all connections. No credentials or IDs are hardcoded in workflow logic; everything lives in the shared credential store described in Section 04.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Dext
Invoice capture and OCR extraction
API key (HTTP header)
Dext Prepare Essential ($49/mo)
Agent 1 (Invoice Intake)
Xero
Duplicate check, bill creation, payment scheduling
OAuth 2.0 (PKCE flow)
Xero Growing ($65/mo)
Agent 1, Agent 2, Agent 3
Slack
Approval routing and exception notifications
OAuth 2.0 (Slack App bot token)
Free tier sufficient; Pro recommended for message history
Agent 2 (Coding and Approval)
Gmail
Invoice intake monitoring and remittance delivery
OAuth 2.0 (Google Workspace)
Google Workspace Business Starter ($6/user/mo)
Agent 1, Agent 3
Google Drive
PO document storage and retrieval
OAuth 2.0 (Google Workspace, shared token with Gmail)
Google Workspace Business Starter (included)
Agent 1 (exception path)
Automation platform
Workflow orchestration layer connecting all tools
Internal service credentials per connector
Paid tier supporting webhooks and scheduled polling ($49/mo)
All agents (orchestration layer)
Before you connect anything: all integrations must be configured and tested against sandbox or development environments before production credentials are entered. Use Dext's sandbox organisation, Xero's Demo Company, a test Slack workspace, and a dedicated Gmail test account. Validate each connection returns expected data before switching to live credentials. Unverified production connections will not be approved for go-live.
02Per-tool integration detail
Dext
Dext is the primary invoice capture layer. It accepts documents via its mobile app, email-to-inbox forwarding, and direct upload. The automation platform consumes extracted invoice data via the Dext REST API and, where available, receives push notifications via webhook when a new document reaches a processed state.
Auth method
API key passed in the X-API-Key HTTP request header. Key is stored in the credential store as DEXT_API_KEY. Never embed inline.
Required scopes
Dext uses key-based auth with no OAuth scope model. The API key must belong to a user with the Accountant or Practice role to access document data across the organisation. Confirm role assignment before first run.
Webhook / trigger setup
Configure a webhook endpoint in Dext under Settings > Integrations > Webhooks. Set the event type to document.published. The automation platform exposes a public HTTPS endpoint to receive the POST payload. Validate the X-Dext-Signature header (HMAC-SHA256, secret stored as DEXT_WEBHOOK_SECRET) on every inbound request and reject any payload that fails signature verification.
Required configuration
Dext organisation ID stored as DEXT_ORG_ID in the credential store. Target inbox or supplier category filter configured in Dext UI to scope which documents trigger the webhook. Do not hardcode the organisation ID in workflow logic.
Rate limits
Dext REST API: 300 requests/minute per API key. At 60 invoices/month the automation generates fewer than 5 API calls per invoice (fetch document, fetch line items, fetch supplier), totalling under 300 calls/month. No throttling logic is required at current volume. Monitor if volume exceeds 1,000 invoices/month.
Constraints
OCR confidence scores below 0.85 on any required field (supplier name, total, invoice number) must route the document to the bookkeeper exception queue rather than proceeding. PDF attachments larger than 10 MB must be rejected with an error notification; Dext enforces this limit on upload.
// Webhook payload (inbound to automation platform)
{
"event": "document.published",
"document_id": "<string>",
"organisation_id": "<string: DEXT_ORG_ID>",
"published_at": "<ISO8601>"
}
// GET /documents/{document_id} response (key fields consumed downstream)
{
"supplier": { "name": "<string>", "id": "<string>" },
"invoice_number": "<string>",
"date": "<YYYY-MM-DD>",
"due_date": "<YYYY-MM-DD>",
"total_amount": "<decimal>",
"tax_amount": "<decimal>",
"currency": "<ISO4217>",
"line_items": [
{ "description": "<string>", "quantity": "<decimal>", "unit_price": "<decimal>", "total": "<decimal>" }
],
"document_url": "<string: signed PDF URL, 15-min TTL>"
}Xero
Xero is the accounting system of record. The automation reads existing bills for duplicate detection, creates new bills with coded line items, and schedules payments. Three distinct API resource groups are used: Invoices (bills), Accounts (chart of accounts), and Payments.
Auth method
OAuth 2.0 with PKCE. Tokens stored in credential store as XERO_ACCESS_TOKEN and XERO_REFRESH_TOKEN. Access tokens expire after 30 minutes; the orchestration layer must implement silent refresh using the refresh token before each API call batch. Refresh tokens expire after 60 days of inactivity; alert the process owner at 50 days.
Required scopes
openid profile email offline_access accounting.transactions accounting.transactions.read accounting.settings.read accounting.contacts.read accounting.attachments accounting.reports.read
Webhook / trigger setup
Xero webhooks are not used in this automation. The automation platform initiates all Xero calls. For the duplicate check, the automation queries the Invoices endpoint synchronously when a new document is received from Dext or Gmail.
Required configuration
XERO_TENANT_ID stored in credential store (retrieved from /connections endpoint after OAuth consent and never hardcoded). XERO_DEFAULT_ACCOUNT_CODE stored as the fallback coding default. AP_PAYMENT_ACCOUNT_ID stored as the bank account ID used for payment scheduling. XERO_AP_CONTACT_GROUP_ID optionally stored to scope supplier lookups.
Rate limits
Xero enforces a 60-calls/minute rate limit per app and a 5,000-calls/day limit. At 60 invoices/month the automation makes approximately 8 Xero API calls per invoice (duplicate query, contact lookup, account list fetch, bill creation, attachment upload, payment creation, confirmation read, report read), totalling around 480 calls/month. Well within daily limits. No throttling required at current volume. Implement a 1-second delay between sequential calls within a single invoice workflow to avoid burst spikes.
Constraints
Bills must be created in DRAFT status first, then transitioned to SUBMITTED before the approval step. Do not create bills in AUTHORISED status directly. Attachment upload uses a separate multipart endpoint (/Invoices/{InvoiceID}/Attachments) and must be called after the bill is created. The Xero API does not support partial payment scheduling via API in all plan tiers; confirm Xero Payroll or payment batch support with the client's Xero plan before wiring the payment step.
// GET /Invoices?where=InvoiceNumber=="{invoice_number}"&ContactID="{contact_id}" (duplicate check)
// Expected: Invoices[] length === 0 for no duplicate
// POST /Invoices (bill creation body)
{
"Type": "ACCPAY",
"Status": "DRAFT",
"Contact": { "ContactID": "<string>" },
"InvoiceNumber": "<string>",
"Date": "<YYYY-MM-DD>",
"DueDate": "<YYYY-MM-DD>",
"CurrencyCode": "<ISO4217>",
"LineItems": [
{
"Description": "<string>",
"Quantity": "<decimal>",
"UnitAmount": "<decimal>",
"AccountCode": "<string>",
"TaxType": "<string>"
}
]
}
// POST /Payments (payment scheduling body)
{
"Invoice": { "InvoiceID": "<string>" },
"Account": { "AccountID": "<string: AP_PAYMENT_ACCOUNT_ID>" },
"Date": "<YYYY-MM-DD: payment run date>",
"Amount": "<decimal>"
}Slack
Slack is the approval routing layer. The automation posts structured Block Kit messages to a designated approver channel or direct message thread. Approvers respond using interactive button actions. The automation listens for action payloads via a registered interactivity endpoint.
Auth method
OAuth 2.0 Slack App installation. Bot token stored as SLACK_BOT_TOKEN. Signing secret stored as SLACK_SIGNING_SECRET for verifying interactivity payloads (HMAC-SHA256 over request body with timestamp; reject any request where the timestamp is older than 5 minutes).
Required scopes
Bot token scopes: chat:write, chat:write.public, im:write, users:read, users:read.email, channels:read, groups:read. User token scopes: none required.
Webhook / trigger setup
Register the automation platform's HTTPS interactivity endpoint under Slack App settings at Interactivity and Shortcuts > Request URL. The endpoint receives HTTP POST payloads when an approver clicks Approve or Query on a Block Kit message. Validate SLACK_SIGNING_SECRET on every inbound payload before processing. Configure SLACK_AP_APPROVAL_CHANNEL_ID (stored in credential store) as the default channel for approval messages; override per approver if DM routing is preferred.
Required configuration
SLACK_BOT_TOKEN in credential store. SLACK_SIGNING_SECRET in credential store. SLACK_AP_APPROVAL_CHANNEL_ID in credential store. SLACK_REMINDER_INTERVAL_HOURS stored as a configurable parameter (default: 24 hours). SLACK_MAX_REMINDERS stored as a configurable parameter (default: 2). Approver Slack user IDs mapped from Xero contact/approver records; mapping table maintained in the credential store as SLACK_APPROVER_MAP (JSON object keyed by department or cost centre code).
Rate limits
Slack API tier 3 methods (chat.postMessage): 1 call/second burst, 50 calls/minute sustained. At 60 invoices/month this automation posts at most 180 messages/month (approval + up to 2 reminders each). No throttling required. Add a 1.1-second delay between sequential chat.postMessage calls in batch scenarios.
Constraints
Block Kit approval messages must include the invoice reference, supplier name, total amount, due date, and suggested account codes in a human-readable format. The Approve and Query buttons must carry the Xero InvoiceID as the action value so the interactivity handler can correlate the response to the correct workflow instance. Do not rely on Slack message timestamps as unique identifiers; use a correlation ID generated by the orchestration layer and embedded in the action value.
// Outbound: POST https://slack.com/api/chat.postMessage
{
"channel": "<SLACK_AP_APPROVAL_CHANNEL_ID or user_id>",
"text": "Invoice approval required: {supplier_name} ${total_amount}",
"blocks": [
{ "type": "section", "text": { "type": "mrkdwn", "text": "*Supplier:* {supplier_name}\n*Invoice #:* {invoice_number}\n*Amount:* ${total_amount}\n*Due:* {due_date}\n*Suggested codes:* {account_codes}" } },
{ "type": "actions", "elements": [
{ "type": "button", "text": { "type": "plain_text", "text": "Approve" }, "style": "primary", "action_id": "ap_approve", "value": "{correlation_id}:{xero_invoice_id}" },
{ "type": "button", "text": { "type": "plain_text", "text": "Query" }, "style": "danger", "action_id": "ap_query", "value": "{correlation_id}:{xero_invoice_id}" }
]}
]
}
// Inbound: Slack interactivity payload (key fields)
{
"type": "block_actions",
"actions": [{ "action_id": "ap_approve|ap_query", "value": "{correlation_id}:{xero_invoice_id}" }],
"user": { "id": "<slack_user_id>", "username": "<string>" }
}Gmail
Gmail serves two roles: inbound monitoring for invoices arriving via email, and outbound sending of remittance advice emails to suppliers after payment. Both roles use the Gmail API under the same OAuth 2.0 token but different method groups.
Auth method
OAuth 2.0 with offline access. Tokens stored as GMAIL_ACCESS_TOKEN and GMAIL_REFRESH_TOKEN in the credential store. Tied to the AP inbox service account or the designated finance Gmail account. Use a dedicated non-personal account for the AP inbox.
Required scopes
https://www.googleapis.com/auth/gmail.readonly (inbound monitoring), https://www.googleapis.com/auth/gmail.send (remittance sending), https://www.googleapis.com/auth/gmail.modify (label application for processed invoices)
Webhook / trigger setup
Use the Gmail API Push Notifications feature (Cloud Pub/Sub) for near-real-time inbox monitoring. Register a Pub/Sub topic and subscription pointing to the automation platform's inbound webhook endpoint. Call gmail.users.watch() with the AP inbox label ID (GMAIL_AP_LABEL_ID stored in credential store) to scope monitoring to the correct label. The watch subscription expires after 7 days; the orchestration layer must renew it automatically by calling watch() again before expiry. As a fallback, configure a polling interval of 5 minutes using gmail.users.messages.list with a q filter of label:{GMAIL_AP_LABEL_ID} is:unread.
Required configuration
GMAIL_AP_INBOX_USER (email address of the AP inbox account) in credential store. GMAIL_AP_LABEL_ID (Gmail label ID for the AP inbox filter) in credential store. GMAIL_PROCESSED_LABEL_ID (Gmail label ID applied after extraction, e.g. AP/Processed) in credential store. GMAIL_REMITTANCE_FROM (display name and address for outbound remittances) in credential store. GMAIL_REMITTANCE_TEMPLATE_ID (ID of the remittance email template stored in the credential store or document store, not hardcoded).
Rate limits
Gmail API quota: 250 quota units/second per user, 1,000,000 units/day. Each message read costs 5 units; each send costs 100 units. At 60 invoices/month and 60 remittances/month the automation consumes roughly 9,300 units/month. No throttling required. Implement exponential backoff on 429 and 503 responses as per Google API client library recommendations.
Constraints
Apply the GMAIL_PROCESSED_LABEL_ID label and remove the unread flag from each invoice email after successful extraction to prevent reprocessing. Attachments must be decoded from base64 before being passed to the Dext upload endpoint or stored temporarily. Temporary attachment files must be deleted from the automation platform's working memory after the Dext document ID is confirmed. Remittance emails must use a plain-text fallback in addition to any HTML body.
// Inbound: Pub/Sub push notification body
{
"message": {
"data": "<base64-encoded: {emailAddress, historyId}>",
"messageId": "<string>"
},
"subscription": "<string>"
}
// GET /gmail/v1/users/{GMAIL_AP_INBOX_USER}/messages/{id}?format=full
// Key fields extracted:
// headers: From, Subject, Date
// parts[].mimeType === 'application/pdf' -> attachment
// Outbound: POST /gmail/v1/users/{GMAIL_AP_INBOX_USER}/messages/send
{
"raw": "<base64url-encoded RFC 2822 message>",
"// message body includes:": "",
"To": "{supplier_email}",
"Subject": "Remittance Advice: {invoice_number} - {supplier_name}",
"Body": "Payment of ${total_amount} for invoice {invoice_number} was processed on {payment_date}."
}Google Drive
Google Drive is used on the exception path of Agent 1 when an invoice references a purchase order. The automation searches the configured PO folder for a matching PO document to surface alongside the flagged invoice in the bookkeeper's exception notification. This is a read-only integration.
Auth method
OAuth 2.0, shared token with the Gmail Google Workspace OAuth app. No separate consent flow required if the Drive scope is added to the existing Gmail OAuth client. Tokens stored as GDRIVE_ACCESS_TOKEN and GDRIVE_REFRESH_TOKEN (may be the same token as Gmail if scopes are combined).
Required scopes
https://www.googleapis.com/auth/drive.readonly
Webhook / trigger setup
No webhook required. Google Drive is queried synchronously when a PO number is detected in the extracted invoice data. The Drive Files.list endpoint is called with a q parameter scoping the search to the GDRIVE_PO_FOLDER_ID and the detected PO number string.
Required configuration
GDRIVE_PO_FOLDER_ID (Google Drive folder ID for the PO document library) stored in credential store. PO file naming convention must follow a consistent pattern (e.g. PO-{po_number}-{supplier_name}.pdf) for the search query to return reliable results. Naming convention documented in the SOP.
Rate limits
Google Drive API: 1,000 requests/100 seconds per user. PO lookups are triggered only on invoices that carry a PO reference; at current volume this is estimated at fewer than 20 queries/month. No throttling required.
Constraints
Drive integration is read-only. No files are created, moved, or deleted by the automation. If no matching PO file is found, the invoice is flagged as a PO-unmatched exception and routed to the bookkeeper without blocking the workflow. Drive results are surfaced as a file link in the exception Slack message; the bookkeeper opens and reviews them manually.
// GET https://www.googleapis.com/drive/v3/files
// ?q='<GDRIVE_PO_FOLDER_ID>' in parents and name contains '<po_number>'
// &fields=files(id,name,webViewLink,createdTime)
// Output passed to Agent 2 exception payload:
{
"po_match_found": true,
"po_file_id": "<string>",
"po_file_name": "<string>",
"po_web_link": "<string: URL for bookkeeper review>"
}Integration and API SpecPage 1 of 3
FS-DOC-05Technical
03Field mappings between tools
The tables below define every field handoff between agents and tools. Source and destination field names are shown in monospace. All fields listed as required must be present before the workflow advances; absent required fields trigger the exception path.
Agent 1 handoff: Dext extraction to Xero duplicate check
Source tool
Source field
Destination tool
Destination field
Dext
`invoice_number`
Xero
`InvoiceNumber` (query param)
Dext
`supplier.id`
Xero
`ContactID` (query param, resolved via contact lookup)
Dext
`supplier.name`
Xero
`Contact.Name` (fallback if no ContactID match)
Dext
`total_amount`
Xero
`SubTotal` + `TotalTax` (validation cross-check only)
Dext
`currency`
Xero
`CurrencyCode`
Dext
`due_date`
Xero
`DueDate`
Agent 1 handoff: Dext extraction to Xero bill creation (post duplicate check)
Source tool
Source field
Destination tool
Destination field
Dext
`supplier.id`
Xero
`Contact.ContactID`
Dext
`invoice_number`
Xero
`InvoiceNumber`
Dext
`due_date`
Xero
`DueDate`
Dext
`currency`
Xero
`CurrencyCode`
Dext
`line_items[].description`
Xero
`LineItems[].Description`
Dext
`line_items[].quantity`
Xero
`LineItems[].Quantity`
Dext
`line_items[].unit_price`
Xero
`LineItems[].UnitAmount`
Dext
`line_items[].total`
Xero
`LineItems[].LineAmount` (validation)
Dext
`tax_amount`
Xero
`TotalTax` (validation cross-check)
Dext
`document_url`
Xero
Attachment (fetched and uploaded separately)
Coding agent (internal)
`suggested_account_code`
Xero
`LineItems[].AccountCode`
Coding agent (internal)
`suggested_tax_type`
Xero
`LineItems[].TaxType`
Agent 1 to Agent 2 handoff: intake record to Slack approval message
Source tool
Source field
Destination tool
Destination field
Xero
`InvoiceID`
Slack
`actions[].value` (correlation payload)
Dext
`supplier.name`
Slack
`blocks[].text` (supplier display)
Dext
`total_amount`
Slack
`blocks[].text` (amount display)
Dext
`due_date`
Slack
`blocks[].text` (due date display)
Dext
`invoice_number`
Slack
`blocks[].text` (reference display)
Coding agent (internal)
`suggested_account_code`
Slack
`blocks[].text` (codes display)
Orchestration layer
`correlation_id`
Slack
`actions[].value` (prefix before InvoiceID)
Agent 2 to Agent 3 handoff: Slack approval response to payment scheduling
Source tool
Source field
Destination tool
Destination field
Slack
`actions[].value` (xero_invoice_id portion)
Xero
`Invoice.InvoiceID`
Slack
`user.id`
Xero
Stored in bill reference field `Reference` (approver audit trail)
Xero
`DueDate`
Xero
`Date` (payment scheduling, mapped to next payment run date)
Xero
`AmountDue`
Xero
`Amount` (payment body)
Xero
`InvoiceID`
Gmail
Embedded in remittance email body as `invoice_number` display
Agent 3 handoff: Xero payment confirmation to Gmail remittance
Source tool
Source field
Destination tool
Destination field
Xero
`Payments[].PaymentID`
Gmail
Remittance reference field in email body
Xero
`Payments[].Date`
Gmail
`payment_date` in remittance template
Xero
`Payments[].Amount`
Gmail
`total_amount` in remittance template
Xero
`Contact.EmailAddress`
Gmail
`To` header
Xero
`InvoiceNumber`
Gmail
`invoice_number` in remittance template and subject line
Xero
`Contact.Name`
Gmail
`supplier_name` in remittance template
All field mappings must be tested against real Xero Demo Company data and a real Dext sandbox document before go-live. Any Xero custom field configuration on the client's chart of accounts must be reviewed and added to this mapping table before the payment step is wired.
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent: Invoice Intake Workflow (Agent 1), Coding and Approval Workflow (Agent 2), Payment and Remittance Workflow (Agent 3). Workflows communicate via a shared internal data store or message queue; no agent calls another agent's workflow directly. A single shared credential store is referenced by all three workflows.
Agent 1 trigger mechanism
Webhook (push). Dext posts a document.published event to the automation platform's HTTPS endpoint. Gmail Pub/Sub push notification triggers the same workflow entry point for email-based invoices. The webhook handler validates both DEXT_WEBHOOK_SECRET (Dext) and Gmail Pub/Sub token before accepting the payload. Fallback: 5-minute polling of the Gmail inbox using label filter if the Pub/Sub subscription has lapsed.
Agent 2 trigger mechanism
Internal event (queue). Agent 1 publishes a structured invoice record (duplicate-clear flag set to true) to an internal workflow queue or trigger endpoint. Agent 2 picks up the record and begins the coding and Slack approval flow. The interactivity response from Slack (approver button click) triggers the next stage of Agent 2 via the registered Slack interactivity HTTPS endpoint. Signature validation (SLACK_SIGNING_SECRET) is applied before any state transition.
Agent 3 trigger mechanism
Internal event (queue). Agent 2 publishes an approved invoice record (containing the Xero InvoiceID and confirmed account codes) to the shared queue. Agent 3 picks up approved records and executes the Xero bill post, payment schedule, and Gmail remittance send in sequence. No external webhook triggers Agent 3.
Slack reminder scheduling
Scheduled poll (internal timer). After posting an approval request, Agent 2 registers a timer event at SLACK_REMINDER_INTERVAL_HOURS (default 24 hours). If no approval response has been received by the timer expiry, Agent 2 posts a reminder message. This repeats up to SLACK_MAX_REMINDERS times (default 2) before escalating to a bookkeeper alert.
Credential store type
The automation platform's native encrypted secret/environment store. All keys are referenced by name in workflow logic; no values are present in workflow configuration files or version-controlled code.
Credential store: all secrets referenced by name in workflow nodes. No inline values permitted.
// CREDENTIAL STORE CONTENTS
// All values injected at runtime. Never committed to version control.
// --- Dext ---
DEXT_API_KEY = "<Dext REST API key, Practice/Accountant role>"
DEXT_ORG_ID = "<Dext organisation ID>"
DEXT_WEBHOOK_SECRET = "<HMAC-SHA256 signing secret from Dext webhook settings>"
// --- Xero ---
XERO_CLIENT_ID = "<OAuth 2.0 app client ID from Xero developer portal>"
XERO_CLIENT_SECRET = "<OAuth 2.0 app client secret>"
XERO_ACCESS_TOKEN = "<Current access token, refreshed every 30 min>"
XERO_REFRESH_TOKEN = "<OAuth 2.0 refresh token, rotated on use>"
XERO_TENANT_ID = "<Xero organisation tenant ID from /connections>"
XERO_DEFAULT_ACCOUNT_CODE = "<Fallback chart-of-accounts code for uncoded lines>"
AP_PAYMENT_ACCOUNT_ID = "<Xero bank account UUID used for payment scheduling>"
XERO_AP_CONTACT_GROUP_ID = "<Optional: Xero contact group ID scoping supplier lookups>"
// --- Slack ---
SLACK_BOT_TOKEN = "<xoxb-... bot token from Slack App OAuth page>"
SLACK_SIGNING_SECRET = "<Signing secret from Slack App Basic Information>"
SLACK_AP_APPROVAL_CHANNEL_ID = "<Slack channel ID for AP approval messages>"
SLACK_REMINDER_INTERVAL_HOURS = "24"
SLACK_MAX_REMINDERS = "2"
SLACK_APPROVER_MAP = "{\"200\":\"U0XXXXXXX\",\"300\":\"U0YYYYYYY\"}"
// Key: Xero cost centre / account code prefix; Value: Slack user ID
// --- Gmail ---
GMAIL_CLIENT_ID = "<Google OAuth 2.0 client ID>"
GMAIL_CLIENT_SECRET = "<Google OAuth 2.0 client secret>"
GMAIL_ACCESS_TOKEN = "<Current access token>"
GMAIL_REFRESH_TOKEN = "<Refresh token with offline_access scope>"
GMAIL_AP_INBOX_USER = "ap@[YourCompany.com]"
GMAIL_AP_LABEL_ID = "<Gmail label ID for AP inbox filter>"
GMAIL_PROCESSED_LABEL_ID = "<Gmail label ID: AP/Processed>"
GMAIL_REMITTANCE_FROM = "\"[YourCompany.com] Accounts Payable\" <ap@[YourCompany.com]>"
GMAIL_REMITTANCE_TEMPLATE_ID = "<ID of remittance email template in document store>"
// --- Google Drive ---
GDRIVE_ACCESS_TOKEN = "<Shared with Gmail token if scopes combined>"
GDRIVE_REFRESH_TOKEN = "<Shared with Gmail token if scopes combined>"
GDRIVE_PO_FOLDER_ID = "<Google Drive folder ID containing PO documents>"
// --- Orchestration layer ---
AUTOMATION_PLATFORM_WEBHOOK_BASE = "https://hooks.[automation-platform-domain]/ap-automation"
INTERNAL_QUEUE_CONNECTION_STRING = "<Platform-native queue or message bus connection>"
OCR_CONFIDENCE_THRESHOLD = "0.85"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. Where retries are specified, the orchestration layer applies exponential backoff starting at the interval stated. After all retries are exhausted, the workflow routes to the manual fallback path and posts an alert to the bookkeeper exception channel.
Integration
Scenario
Required behaviour
Dext webhook
Signature validation fails on inbound payload
Reject with HTTP 401. Log the rejection including the raw X-Dext-Signature header. Do not process the payload. Alert the FullSpec monitoring channel. No retry; a valid re-delivery from Dext will follow if the document is genuine.
Dext API: document fetch
GET /documents/{id} returns 404 or 503
Retry 3 times with exponential backoff: 30 s, 60 s, 120 s. If all retries fail, post a Slack alert to the bookkeeper exception channel with the document ID and instruct manual retrieval from Dext. Mark the workflow instance as FAILED_INTAKE.
Dext OCR confidence
Any required field confidence score below 0.85
Route to bookkeeper exception queue immediately without retrying. Post a Slack message with the low-confidence field names and a link to the Dext document for manual correction. Workflow pauses until bookkeeper confirms corrected data.
Xero OAuth token
Access token expired (401 Unauthorized on any Xero call)
Attempt silent token refresh using XERO_REFRESH_TOKEN before retrying the failed call. If refresh also fails (401), alert the process owner and support@gofullspec.com immediately. All Xero-dependent workflow steps pause until token is re-established. Do not attempt further Xero calls.
Xero: duplicate check
Xero API returns 429 (rate limit) during duplicate query
Retry after 60 seconds, then 120 seconds, then 240 seconds (max 3 retries). If limit persists, log the invoice and add to a retry queue processed in the next polling cycle (5 minutes). Alert the bookkeeper if delay exceeds 15 minutes.
Xero: duplicate found
Duplicate invoice detected (InvoiceNumber + ContactID match in DRAFT or SUBMITTED status)
Do not create a new bill. Route the invoice to the bookkeeper exception channel in Slack with the existing Xero bill URL and both invoice details. Mark the workflow instance as DUPLICATE_FLAGGED. No retry; bookkeeper resolves manually.
Xero: bill creation
POST /Invoices returns 400 (validation error, e.g. invalid AccountCode)
Log the full Xero error response. Fall back to creating the bill with XERO_DEFAULT_ACCOUNT_CODE on the failing line item. Flag the bill in Xero as needing code review. Notify the bookkeeper via Slack with the validation error detail. Do not block the approval step.
Slack: approval message delivery
chat.postMessage returns error (channel_not_found, not_in_channel)
Retry once after 10 seconds. If still failing, attempt delivery as a direct message to the approver's Slack user ID from SLACK_APPROVER_MAP. If DM also fails, send fallback approval request via Gmail to the approver's email address resolved from Xero contact record. Log the Slack failure.
Slack: approval timeout
No approve or query action received within (SLACK_REMINDER_INTERVAL_HOURS x SLACK_MAX_REMINDERS) hours
After max reminders are exhausted, escalate to the bookkeeper and finance manager via Slack direct message. Post the invoice details and request a decision. Log the invoice as APPROVAL_ESCALATED. Workflow pauses; it does not auto-approve or auto-reject.
Gmail: Pub/Sub watch expiry
watch() subscription has expired (7-day TTL) and push notifications have stopped
The orchestration layer checks watch expiry daily and renews it 24 hours before expiry. If renewal fails, fall back to 5-minute polling using gmail.users.messages.list with label filter. Alert support@gofullspec.com if polling fallback is active for more than 2 polling cycles.
Gmail: remittance send failure
POST /messages/send returns 403 (quota exceeded) or 500 (server error)
Retry 3 times with backoff: 2 min, 5 min, 10 min. If all retries fail, log the failure and post a Slack alert to the bookkeeper with the supplier email address and invoice reference, asking for a manual remittance to be sent. Mark the workflow instance as REMITTANCE_FAILED. Do not silently skip the remittance step.
Google Drive: PO lookup
Drive API returns 404 or no files match the PO number query
Do not block the workflow. Flag the invoice as PO_UNMATCHED in the Agent 2 payload. Include a note in the Slack approval message that no PO document was found. The approver can still approve; the bookkeeper reviews the discrepancy as part of the exception resolution process.
Unhandled exceptions: any error condition not covered by the rows above must be caught by a global exception handler in each workflow. The handler must log the full error payload (timestamp, workflow instance ID, step name, raw error), post a generic alert to the bookkeeper Slack exception channel, and mark the workflow instance as UNHANDLED_EXCEPTION. Silent failures are not acceptable at any step. Contact support@gofullspec.com if an unhandled exception pattern recurs.
Integration and API SpecPage 3 of 3