Back to Affiliate Programme Management

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

Affiliate Programme Management

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

This document provides the complete integration and API specification for the Affiliate Programme Management automation. It is written for the FullSpec build team and covers every tool connection, required OAuth scopes, webhook configuration, field mappings between agents, credential store contents, and defined error-handling behaviour. Nothing in this document should be interpreted by the process owner as an operational guide: that is covered in the SOP/Runbook. All build decisions and integration choices described here are the responsibility of the FullSpec team.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
Tapfiliate
Source of conversion events and affiliate profile data
API key (header)
Pro ($89/mo) for webhook support
Agent 1 (Conversion Validation)
Google Sheets
Commission log and audit trail; weekly digest data source
OAuth 2.0 (service account recommended)
Free (Google Workspace or personal)
Agent 1, Agent 3
HubSpot
CRM deal creation and affiliate attribution recording
OAuth 2.0 (private app token)
Free CRM tier (Deals object required)
Agent 2 (Payout and Communications)
Xero
Commission bill creation and payout, awaiting finance approval
OAuth 2.0 (authorization code flow)
Starter or above
Agent 2
Gmail
Payment confirmation emails and affiliate onboarding sequences
OAuth 2.0 (Google identity platform)
Any Google Workspace tier
Agent 2
Slack
Weekly performance digest posted to internal channel
OAuth 2.0 (Bot token via Slack app)
Free or Pro (Bot scopes available on both)
Agent 3 (Reporting)
Orchestration layer
Hosts all agent workflows, credential store, triggers, and retry logic
Internal (managed by FullSpec)
Per build agreement
All agents
Before you connect anything: all six tool connections must be tested in sandbox or developer mode before production credentials are stored. Tapfiliate supports a test environment via API key scoped to a sandbox account. HubSpot provides a developer sandbox. Xero provides a demo company. Gmail and Google Sheets connections should be tested against a dedicated test Google account, not the live programme mailbox. Slack connections should be tested in a private non-production channel. No production data should be written or read until sandbox validation passes for every connection.
Integration and API SpecPage 1 of 5
FS-DOC-05Technical

02Per-tool integration detail

Tapfiliate

Provides conversion events and affiliate profile data to Agent 1. The connection is the entry point for the entire automation. Webhook delivery is preferred; polling is the fallback for plans that do not expose webhook endpoints reliably.

Auth method
API key passed as request header: Api-Key: {TAPFILIATE_API_KEY}. Key is stored in the credential store and never hardcoded.
Required scopes
Tapfiliate uses API key auth with no granular OAuth scopes. The key must belong to an account with Administrator or Manager role to access GET /conversions, GET /affiliates/{id}, and POST /conversions/{id}/disapprove.
Webhook setup
In Tapfiliate dashboard: Settings > Integrations > Webhooks. Create a webhook pointing to the orchestration layer's inbound webhook URL. Event: conversion_approved. Secret token stored as TAPFILIATE_WEBHOOK_SECRET. The orchestration layer must validate the X-Tapfiliate-Signature header (HMAC-SHA256 of the raw payload body using the shared secret) before processing any event.
Polling fallback
If webhook delivery is unreliable, poll GET /conversions?date_from={last_run_timestamp}&limit=100 every 5 minutes. Store last_run_timestamp in workflow state. De-duplicate by conversion ID against the Google Sheets log before processing.
Required configuration
TAPFILIATE_API_KEY stored in credential store. TAPFILIATE_WEBHOOK_SECRET stored in credential store. Commission tier rules stored as a lookup table in workflow configuration (not hardcoded): tier name mapped to commission percentage. Tier table must be updated manually when rules change.
Rate limits
Tapfiliate enforces 100 requests/minute on the Pro plan. At ~120 conversions/month (approx. 4/day), throttling is not required under normal operation. Build in a 600ms delay between sequential API calls during batch validation runs to stay well within limits.
Constraints
Refund detection relies on conversion status field. Disapproved conversions must be excluded; the agent must check status == 'approved' before writing to the log. Duplicate detection is by conversion ID: if the ID already exists in the Sheets log, skip and log a warning.
// Inbound webhook payload (trimmed)
{
  "event": "conversion_approved",
  "conversion": {
    "id": "conv_abc123",
    "affiliate": { "id": "aff_001", "email": "partner@example.com" },
    "amount": 250.00,
    "currency": "USD",
    "commission": 25.00,
    "created_at": "2024-05-01T09:14:22Z",
    "status": "approved"
  }
}
Google Sheets

Serves as the commission log and audit trail (written by Agent 1) and as the weekly data source for the performance digest (read by Agent 3). A single spreadsheet with two named tabs is required.

Auth method
OAuth 2.0 using a Google service account. The service account JSON key is stored in the credential store as GSHEETS_SERVICE_ACCOUNT_JSON. The target spreadsheet must be shared with the service account email address (Editor permission).
Required scopes
https://www.googleapis.com/auth/spreadsheets (read and write to spreadsheets). https://www.googleapis.com/auth/drive.file (access files created or opened by the app, used for initial setup only).
Webhook/trigger setup
No webhook available. Agent 3 reads the sheet on a cron schedule (Monday 07:00 local time). Agent 1 writes on each validated conversion event.
Required configuration
GSHEETS_SPREADSHEET_ID stored in credential store (not hardcoded). Tab 1 name: CommissionLog. Tab 2 name: WeeklyDigest. CommissionLog column structure: A=conversion_id, B=affiliate_id, C=affiliate_email, D=sale_amount_usd, E=commission_rate_pct, F=commission_amount_usd, G=status, H=hubspot_deal_id, I=xero_bill_id, J=logged_at. Row 1 is a frozen header row. WeeklyDigest tab is written by Agent 3 as a staging area before Slack post.
Rate limits
Google Sheets API allows 300 read requests/minute and 300 write requests/minute per project. At current volume (120 conversions/month) throttling is not required. Use batch append (values:batchUpdate) for any multi-row writes rather than individual row calls.
Constraints
The spreadsheet ID must never be hardcoded in workflow logic. All reads and writes reference columns by header name resolved at runtime. If a column is missing, the agent must halt and alert via Slack rather than writing to a wrong column.
// Row written to CommissionLog tab by Agent 1
[
  "conv_abc123",       // A: conversion_id
  "aff_001",           // B: affiliate_id
  "partner@example.com", // C: affiliate_email
  250.00,              // D: sale_amount_usd
  10,                  // E: commission_rate_pct
  25.00,               // F: commission_amount_usd
  "pending_payout",   // G: status
  "",                 // H: hubspot_deal_id (populated by Agent 2)
  "",                 // I: xero_bill_id (populated by Agent 2)
  "2024-05-01T09:15:00Z" // J: logged_at
]
HubSpot

Receives deal creation and update requests from Agent 2 to record affiliate attribution against each validated conversion. Uses the private app token auth model introduced in HubSpot's v3 API.

Auth method
Private app token (Bearer). Stored as HUBSPOT_PRIVATE_APP_TOKEN in the credential store. Do not use legacy API keys (deprecated). The private app is created under Settings > Integrations > Private Apps in the HubSpot account.
Required scopes
crm.objects.deals.read, crm.objects.deals.write, crm.objects.contacts.read, crm.objects.contacts.write, crm.schemas.deals.read. If custom properties are used on the Deal object, also request crm.schemas.custom.read.
Webhook/trigger setup
HubSpot is not a trigger source in this automation. Agent 2 calls HubSpot outbound only. No inbound webhook from HubSpot is required.
Required configuration
HUBSPOT_PRIVATE_APP_TOKEN in credential store. HUBSPOT_PIPELINE_ID stored in credential store: the ID of the Affiliate Conversions pipeline (not the default sales pipeline). HUBSPOT_STAGE_ID_COMMISSION_LOGGED: deal stage ID for 'Commission Logged'. HUBSPOT_STAGE_ID_PAID: deal stage ID for 'Paid'. Custom deal properties required: affiliate_id (single-line text), commission_amount (number), conversion_id (single-line text), affiliate_tier (single-line text). These must be created in HubSpot before build begins.
Rate limits
HubSpot v3 API: 100 requests/10 seconds, 250,000 requests/day on free tier. At current volume throttling is not required. Use PATCH /crm/v3/objects/deals/{dealId} to update existing deals; search by conversion_id custom property before creating to avoid duplicates.
Constraints
Pipeline and stage IDs are environment-specific. They must be retrieved from the HubSpot account during setup and stored in the credential store. Hardcoding stage names as strings and resolving at runtime is not acceptable: use IDs. If the deal already exists (matched on conversion_id), update it rather than creating a duplicate.
// POST /crm/v3/objects/deals  (create deal)
{
  "properties": {
    "dealname": "Affiliate Commission: aff_001 - conv_abc123",
    "pipeline": "{HUBSPOT_PIPELINE_ID}",
    "dealstage": "{HUBSPOT_STAGE_ID_COMMISSION_LOGGED}",
    "amount": 25.00,
    "affiliate_id": "aff_001",
    "commission_amount": 25.00,
    "conversion_id": "conv_abc123",
    "affiliate_tier": "Silver"
  }
}
Integration and API SpecPage 2 of 5
FS-DOC-05Technical
Xero

Receives bill creation requests from Agent 2 at each monthly payout cycle close. One bill is raised per affiliate with the total commission owed for the month. Finance approves and releases payment manually: the automation does not process bank transfers.

Auth method
OAuth 2.0 authorization code flow with PKCE. Tokens stored as XERO_ACCESS_TOKEN and XERO_REFRESH_TOKEN in the credential store. Access tokens expire after 30 minutes; the orchestration layer must implement silent token refresh using the refresh token before each Xero API call. XERO_CLIENT_ID and XERO_CLIENT_SECRET stored in credential store.
Required scopes
openid, profile, email, accounting.transactions, accounting.contacts.read. The accounting.transactions scope covers bill creation (POST /Bills). accounting.contacts.read is required to look up affiliate contact records in Xero by email before creating bills.
Webhook/trigger setup
Xero does not trigger the automation. Agent 2 is triggered by a monthly schedule cron. However, to detect bill approval (which triggers the Gmail confirmation step), configure a Xero webhook: Xero Developer Portal > My Apps > {app} > Webhooks. Event: Invoice.AUTHORISED. Validate incoming requests using the Xero-Signature header (SHA-256 HMAC with XERO_WEBHOOK_KEY). Store XERO_WEBHOOK_KEY in credential store.
Required configuration
XERO_TENANT_ID stored in credential store (retrieved from GET /connections after OAuth). XERO_ACCOUNT_CODE_COMMISSIONS: the Xero account code used for commission expense bills (e.g. 6200). XERO_CONTACT_PREFIX: naming convention for affiliate contacts in Xero (e.g. 'Affiliate: {affiliate_email}'). Monthly payout cron: last calendar day of each month at 06:00 UTC.
Rate limits
Xero API: 60 requests/minute, 5000 requests/day (Standard). At 120 affiliates/month (one bill each), a single payout run will make approximately 120 POST requests plus up to 120 contact lookups, totalling ~240 requests. This completes within 4 minutes at 60 req/min. No throttling concern at current volume. Build in 1-second delay between bill creation calls during batch run.
Constraints
Xero bill creation requires an existing Contact record for each affiliate. Agent 2 must perform a contact lookup by email before creating a bill; if no contact exists, create one using POST /Contacts. Bills are created with Status: DRAFT so finance can review before approving. The automation must never set Status: AUTHORISED programmatically.
// POST /Bills  (create draft bill for one affiliate)
{
  "Type": "ACCPAY",
  "Contact": { "ContactID": "{xero_contact_id}" },
  "DueDateString": "2024-05-10",
  "Status": "DRAFT",
  "LineAmountTypes": "Exclusive",
  "LineItems": [
    {
      "Description": "Affiliate commission - May 2024 - aff_001",
      "Quantity": 1,
      "UnitAmount": 125.00,
      "AccountCode": "{XERO_ACCOUNT_CODE_COMMISSIONS}"
    }
  ],
  "Reference": "AFFPAY-2024-05-aff_001"
}
Gmail

Used by Agent 2 to send payment confirmation emails to affiliates after Xero bill approval, and to send the onboarding sequence when a new affiliate is approved in Tapfiliate. All emails are sent from the authorised programme mailbox.

Auth method
OAuth 2.0 via Google Identity Platform. Credentials stored as GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REFRESH_TOKEN in the credential store. The refresh token is obtained by completing the OAuth consent flow for the specific sender mailbox. Access tokens must be silently refreshed before each send operation.
Required scopes
https://www.googleapis.com/auth/gmail.send (send email on behalf of the authorised account only). Do not request https://www.googleapis.com/auth/gmail.modify or broader scopes: least-privilege applies.
Webhook/trigger setup
Gmail is an output-only tool in this automation. No inbound Gmail trigger is used. Payment confirmation emails are triggered by the Xero Invoice.AUTHORISED webhook event. Onboarding emails are triggered by the Tapfiliate conversion_approved event where the affiliate is newly registered (affiliate created_at within last 24 hours).
Required configuration
GMAIL_SENDER_ADDRESS: the from address used for all outbound emails. Email templates stored in workflow configuration (not hardcoded in logic): TEMPLATE_PAYMENT_CONFIRMATION containing placeholders {{affiliate_name}}, {{payout_amount}}, {{payment_reference}}, {{expected_transfer_date}}. TEMPLATE_ONBOARDING_WELCOME containing placeholders {{affiliate_name}}, {{tracking_link}}, {{programme_terms_url}}, {{support_email}}. Templates must be stored in the credential/config store and referenced by key.
Rate limits
Gmail API: 250 quota units/second per user, 1,000,000 quota units/day. Each send costs 100 quota units. At 120 affiliates/month the payout confirmation batch uses 12,000 quota units, well within daily limits. No throttling required. Add 500ms delay between sends in a batch to avoid triggering spam filters.
Constraints
All emails must include an unsubscribe link or reply instruction per CAN-SPAM requirements. The sender address must match the authorised OAuth account exactly. If a send fails due to a bounce or invalid address, the failure must be logged to the CommissionLog sheet (status column updated to 'email_failed') and a Slack alert must fire.
// Gmail API send payload (payment confirmation)
{
  "to": "partner@example.com",
  "from": "{GMAIL_SENDER_ADDRESS}",
  "subject": "Your affiliate commission payment: $125.00",
  "body_html": "<p>Hi {{affiliate_name}}, ...</p>",  // rendered from TEMPLATE_PAYMENT_CONFIRMATION
  "reply_to": "{GMAIL_SENDER_ADDRESS}"
}
Slack

Receives a weekly performance digest post from Agent 3 every Monday morning. Also receives error alert messages from any agent when a failure requires human attention.

Auth method
OAuth 2.0 Bot token. A dedicated Slack app is created in the Slack API portal. The bot token is stored as SLACK_BOT_TOKEN in the credential store. The app must be installed to the workspace and invited to the target channels.
Required scopes
Bot token scopes: chat:write (post messages to channels the bot is a member of), chat:write.public (post to public channels without joining), channels:read (list channels to resolve channel name to ID at setup). Do not request broader scopes.
Webhook/trigger setup
No inbound Slack webhook is used. Agent 3 posts outbound via the Slack Web API (chat.postMessage). SLACK_DIGEST_CHANNEL_ID stored in credential store (resolved from channel name at setup, stored as ID not name). SLACK_ALERTS_CHANNEL_ID stored in credential store for error alerts from all agents.
Required configuration
SLACK_BOT_TOKEN in credential store. SLACK_DIGEST_CHANNEL_ID: ID of the #affiliate-performance channel (or equivalent). SLACK_ALERTS_CHANNEL_ID: ID of the #automation-alerts channel. Weekly digest cron: Monday 07:00 in the business local timezone. Message format: Block Kit JSON with a header block, section blocks per KPI, and a divider.
Rate limits
Slack Web API: Tier 3 rate limit on chat.postMessage: 1+ requests/second. At 1 post/week for the digest plus occasional error alerts, there is no throttling concern whatsoever.
Constraints
Channel IDs must be stored, not channel names: names can be changed by workspace admins and would silently break posting. The bot must be a member of both channels before deployment. Block Kit payloads must not exceed 50 blocks per message.
// chat.postMessage payload (weekly digest)
{
  "channel": "{SLACK_DIGEST_CHANNEL_ID}",
  "blocks": [
    { "type": "header", "text": { "type": "plain_text", "text": "Affiliate Performance: w/e 5 May 2024" } },
    { "type": "section", "fields": [
      { "type": "mrkdwn", "text": "*Total conversions:* 31" },
      { "type": "mrkdwn", "text": "*Total commission accrued:* $875.00" },
      { "type": "mrkdwn", "text": "*Top affiliate:* partner@example.com (8 conversions)" }
    ]},
    { "type": "divider" }
  ]
}
Integration and API SpecPage 3 of 5
FS-DOC-05Technical

03Field mappings between tools

The tables below document every field-level mapping at each agent handoff point. Source and destination field names are shown in exact monospace format as they appear in the respective API payloads or sheet columns. All transformations applied between source and destination are noted in the destination field column.

Handoff A: Tapfiliate to Google Sheets (Agent 1, Conversion Validation Agent)

Source tool
Source field
Destination tool
Destination field
Tapfiliate
conversion.id
Google Sheets
CommissionLog[A]: conversion_id
Tapfiliate
conversion.affiliate.id
Google Sheets
CommissionLog[B]: affiliate_id
Tapfiliate
conversion.affiliate.email
Google Sheets
CommissionLog[C]: affiliate_email
Tapfiliate
conversion.amount
Google Sheets
CommissionLog[D]: sale_amount_usd
Tapfiliate
conversion.commission (or tier lookup)
Google Sheets
CommissionLog[E]: commission_rate_pct (resolved from tier rules table)
Tapfiliate
conversion.amount x commission_rate_pct
Google Sheets
CommissionLog[F]: commission_amount_usd (calculated by agent)
Tapfiliate
conversion.status (validated == approved)
Google Sheets
CommissionLog[G]: status (set to 'pending_payout')
Tapfiliate
conversion.created_at
Google Sheets
CommissionLog[J]: logged_at (ISO 8601 UTC)

Handoff B: Google Sheets to HubSpot (Agent 2, Payout and Communications Agent)

Source tool
Source field
Destination tool
Destination field
Google Sheets
CommissionLog[A]: conversion_id
HubSpot
deals.properties.conversion_id
Google Sheets
CommissionLog[B]: affiliate_id
HubSpot
deals.properties.affiliate_id
Google Sheets
CommissionLog[C]: affiliate_email
HubSpot
deals.properties.dealname (appended as 'Affiliate Commission: {affiliate_id} - {conversion_id}')
Google Sheets
CommissionLog[F]: commission_amount_usd
HubSpot
deals.properties.amount
Google Sheets
CommissionLog[F]: commission_amount_usd
HubSpot
deals.properties.commission_amount
Google Sheets
CommissionLog[E]: commission_rate_pct (tier label)
HubSpot
deals.properties.affiliate_tier
Workflow config
{HUBSPOT_PIPELINE_ID}
HubSpot
deals.properties.pipeline
Workflow config
{HUBSPOT_STAGE_ID_COMMISSION_LOGGED}
HubSpot
deals.properties.dealstage

Handoff C: Google Sheets to Xero (Agent 2, monthly payout run)

Source tool
Source field
Destination tool
Destination field
Google Sheets
CommissionLog[B]: affiliate_id (grouped)
Xero
Bills.Contact.ContactID (resolved via GET /Contacts?emailAddress={affiliate_email})
Google Sheets
CommissionLog[C]: affiliate_email
Xero
Bills.Contact lookup key
Google Sheets
SUM(commission_amount_usd) by affiliate_id
Xero
Bills.LineItems[0].UnitAmount
Google Sheets
Derived: 'Affiliate commission - {month} - {affiliate_id}'
Xero
Bills.LineItems[0].Description
Google Sheets
Derived: 'AFFPAY-{YYYY-MM}-{affiliate_id}'
Xero
Bills.Reference
Workflow config
{XERO_ACCOUNT_CODE_COMMISSIONS}
Xero
Bills.LineItems[0].AccountCode
Workflow config
Payout due date (cycle close + 10 days)
Xero
Bills.DueDateString

Handoff D: Xero bill approval to Gmail (Agent 2, payment confirmation)

Source tool
Source field
Destination tool
Destination field
Xero webhook
payload.invoiceID
Google Sheets
CommissionLog[I]: xero_bill_id (written back on approval event)
Xero webhook
payload.contact.emailAddress
Gmail
send.to
Xero webhook
payload.contact.name
Gmail
template placeholder: {{affiliate_name}}
Xero webhook
payload.total
Gmail
template placeholder: {{payout_amount}}
Xero webhook
payload.reference
Gmail
template placeholder: {{payment_reference}}
Workflow config
Cycle due date
Gmail
template placeholder: {{expected_transfer_date}}

Handoff E: Google Sheets to Slack (Agent 3, weekly digest)

Source tool
Source field
Destination tool
Destination field
Google Sheets
COUNT(CommissionLog[A]) WHERE logged_at >= 7 days ago
Slack
Block Kit section field: *Total conversions*
Google Sheets
SUM(CommissionLog[F]) WHERE logged_at >= 7 days ago
Slack
Block Kit section field: *Total commission accrued*
Google Sheets
affiliate_id with MAX(COUNT) in 7-day window
Slack
Block Kit section field: *Top affiliate*
Google Sheets
CommissionLog[C]: affiliate_email for top affiliate
Slack
Block Kit section field: *Top affiliate* (appended email)
Google Sheets
COUNT(CommissionLog[G]) WHERE status == 'pending_payout'
Slack
Block Kit section field: *Pending payouts*

04Build stack and orchestration

Orchestration layout
Three discrete workflows, one per agent: Workflow 1 (Conversion Validation Agent), Workflow 2 (Payout and Communications Agent), Workflow 3 (Reporting Agent). All three share a single credential store managed by the orchestration layer. No workflow calls another workflow directly: they communicate via Google Sheets as the shared data layer.
Agent 1 trigger
Webhook: inbound HTTP POST from Tapfiliate on conversion_approved event. Signature validated using HMAC-SHA256 against TAPFILIATE_WEBHOOK_SECRET before any processing. Fallback: scheduled poll every 5 minutes against GET /conversions if webhook delivery is confirmed unreliable during sandbox testing.
Agent 2 trigger (payout cycle)
Cron schedule: last calendar day of each month at 06:00 UTC. Workflow reads all CommissionLog rows with status == 'pending_payout', groups by affiliate_id, creates Xero bills, then waits for Xero Invoice.AUTHORISED webhook to trigger the Gmail confirmation step.
Agent 2 trigger (onboarding)
Shares the same inbound webhook endpoint as Agent 1. On a conversion_approved event, Agent 2 checks whether the affiliate account created_at is within the last 24 hours. If true, the onboarding email sequence fires immediately after Agent 1 writes the commission log row.
Agent 3 trigger
Cron schedule: every Monday at 07:00 in the configured local timezone. Reads the Google Sheets CommissionLog, computes the weekly aggregates, and posts to Slack via chat.postMessage.
Credential store access
All secrets are injected into workflow steps as environment variables or secure vault references at runtime. No secret value is written into workflow logic, step configuration, or log output. Credential store access is restricted to the orchestration layer service account.
Xero token refresh
A dedicated pre-step in any Xero-connected workflow node checks token expiry and calls POST /token with grant_type=refresh_token before the main API call. Updated access and refresh tokens are written back to the credential store immediately.
Credential store: all entries injected at runtime. No value is written into workflow logic or logs.
// Credential store contents (all values are secret references, not literal values)

TAPFILIATE_API_KEY              = <secret>   // Tapfiliate API key (Administrator role)
TAPFILIATE_WEBHOOK_SECRET       = <secret>   // HMAC-SHA256 shared secret for webhook validation

GSHEETS_SERVICE_ACCOUNT_JSON    = <secret>   // Google service account JSON key (base64 encoded)
GSHEETS_SPREADSHEET_ID          = <secret>   // Target spreadsheet ID (not URL, not hardcoded)

HUBSPOT_PRIVATE_APP_TOKEN       = <secret>   // HubSpot private app Bearer token
HUBSPOT_PIPELINE_ID             = <config>   // Affiliate Conversions pipeline ID
HUBSPOT_STAGE_ID_COMMISSION_LOGGED = <config> // Deal stage: Commission Logged
HUBSPOT_STAGE_ID_PAID           = <config>   // Deal stage: Paid

XERO_CLIENT_ID                  = <secret>   // Xero OAuth app client ID
XERO_CLIENT_SECRET              = <secret>   // Xero OAuth app client secret
XERO_ACCESS_TOKEN               = <secret>   // Short-lived access token (refreshed automatically)
XERO_REFRESH_TOKEN              = <secret>   // Long-lived refresh token
XERO_TENANT_ID                  = <config>   // Xero organisation tenant ID
XERO_ACCOUNT_CODE_COMMISSIONS   = <config>   // Xero chart of accounts code for commission expense
XERO_WEBHOOK_KEY                = <secret>   // HMAC key for Xero webhook signature validation

GMAIL_CLIENT_ID                 = <secret>   // Gmail OAuth client ID
GMAIL_CLIENT_SECRET             = <secret>   // Gmail OAuth client secret
GMAIL_REFRESH_TOKEN             = <secret>   // Gmail OAuth refresh token for sender mailbox
GMAIL_SENDER_ADDRESS            = <config>   // From address for all outbound programme emails
TEMPLATE_PAYMENT_CONFIRMATION   = <config>   // HTML email template (payment confirmation)
TEMPLATE_ONBOARDING_WELCOME     = <config>   // HTML email template (affiliate onboarding)

SLACK_BOT_TOKEN                 = <secret>   // Slack bot OAuth token (xoxb-...)
SLACK_DIGEST_CHANNEL_ID         = <config>   // Slack channel ID for weekly digest
SLACK_ALERTS_CHANNEL_ID         = <config>   // Slack channel ID for automation error alerts
Integration and API SpecPage 4 of 5
FS-DOC-05Technical

05Error handling and retry logic

Unhandled exceptions must never fail silently. Every integration point listed below has a defined failure behaviour. Any error not covered by the retry and fallback logic below must be caught by a global exception handler that posts to SLACK_ALERTS_CHANNEL_ID with the workflow name, step name, error code, and a truncated payload reference. Silent failure is not an acceptable outcome at any step.
Integration
Scenario
Required behaviour
Tapfiliate webhook
Webhook signature validation fails (HMAC mismatch)
Return HTTP 401 immediately. Do not process the payload. Log the event with timestamp and raw X-Tapfiliate-Signature value to the orchestration log. Fire Slack alert to SLACK_ALERTS_CHANNEL_ID. Do not retry: a mismatched signature indicates a tampered or incorrectly configured request.
Tapfiliate API (polling fallback)
GET /conversions returns HTTP 429 (rate limit)
Pause execution for 60 seconds, then retry the request. Retry up to 3 times with 60s, 120s, 240s backoff. If still failing after 3 retries, skip the current poll cycle, log the failure, and fire a Slack alert. The next scheduled poll will catch any missed conversions.
Tapfiliate API
GET /conversions returns HTTP 5xx (server error)
Retry immediately once. If second attempt also fails, wait 5 minutes and retry once more. After two failures, skip the poll cycle and fire a Slack alert. Do not mark conversions as processed until a successful response is received.
Google Sheets write (Agent 1)
Sheets API returns HTTP 503 or quota exceeded
Retry with exponential backoff: 10s, 30s, 90s. After 3 failed retries, hold the conversion record in workflow memory and attempt to write again on the next trigger cycle. If the record cannot be written after 6 hours, fire a Slack alert with the full conversion payload so it can be entered manually.
Google Sheets column resolution (Agent 1 or 3)
Expected column header not found in CommissionLog tab
Halt execution immediately. Do not write any row data. Fire a Slack alert with the missing column name and sheet ID. This indicates the sheet structure has been manually modified and must be investigated before the agent resumes.
HubSpot deal creation (Agent 2)
POST /crm/v3/objects/deals returns HTTP 429
Wait 10 seconds and retry. Retry up to 3 times with 10s, 30s, 60s backoff. After 3 failures, log the conversion_id and affiliate_id to a 'hubspot_failed' queue in Google Sheets (CommissionLog column G updated to 'hubspot_failed'). Fire Slack alert. The failed HubSpot writes must be retried in the next scheduled run.
HubSpot deal creation (Agent 2)
Duplicate deal detected (conversion_id already exists as custom property)
Do not create a second deal. Switch to PATCH /crm/v3/objects/deals/{dealId} to update the existing record with the latest commission_amount and dealstage. Log the update action to the orchestration log at INFO level.
Xero token refresh
POST /token returns HTTP 400 (invalid refresh token)
Halt all Xero-dependent steps immediately. Fire Slack alert to SLACK_ALERTS_CHANNEL_ID with message: 'Xero OAuth refresh token has expired. Manual re-authorisation required. No Xero bills will be created until resolved.' Do not attempt Xero API calls until the token is manually refreshed and the new tokens stored.
Xero bill creation (Agent 2, payout batch)
POST /Bills returns HTTP 400 (validation error, e.g. missing contact)
Log the affiliate_id, error message, and Xero response body to a 'xero_failed' tab in Google Sheets. Skip that affiliate's bill and continue creating bills for the remaining affiliates. Fire a single batched Slack alert listing all failed affiliates after the payout run completes. Do not stop the entire batch for one failure.
Gmail send (Agent 2)
Send returns HTTP 429 or daily quota exceeded
Pause 30 seconds and retry. If quota is genuinely exhausted for the day, queue the remaining sends in a 'gmail_pending' list in Google Sheets and retry the following morning at 08:00 UTC. Update CommissionLog[G] to 'email_pending' for affected rows. Fire Slack alert with count of pending emails.
Gmail send (Agent 2)
Send fails due to invalid recipient address (HTTP 400 or bounce)
Log the affiliate_email and error to CommissionLog[G] as 'email_failed'. Do not retry: the address is invalid. Fire a Slack alert with the affiliate_id and email address so the programme manager can correct the address in Tapfiliate and trigger a manual resend.
Slack post (Agent 3)
chat.postMessage returns HTTP 404 (channel not found or bot not a member)
Retry once after 5 seconds. If still failing, fall back to posting to SLACK_ALERTS_CHANNEL_ID with the full digest payload attached as a text block. Log the channel ID and error. This ensures the digest is never silently lost even if the primary channel is renamed or the bot is removed.
For questions about this specification, contact the FullSpec team at support@gofullspec.com. Reference the process name 'Affiliate Programme Management' and the document code FS-DOC-05 in all correspondence.
Integration and API SpecPage 5 of 5

More documents for this process

Every document generated for Affiliate Programme Management.

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