Back to Subscription & Recurring Billing

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

Subscription and Recurring Billing

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

This document is the authoritative technical reference for every integration point in the Subscription and Recurring Billing automation. It covers authentication methods, required API scopes, webhook configuration, field mappings across agent handoffs, credential-store layout, and error-handling behaviour for all five tools: Stripe, Xero, HubSpot, Gmail, and Slack. The orchestration layer is not named here because the build-platform decision is finalised during the Discovery stage. All specifics in this document apply regardless of which workflow automation tool is selected. FullSpec configures and maintains every integration described below; your team's responsibility is to provide credential access as documented in the Developer Handover Pack.

01Tool inventory

Tool
Role in automation
Auth method
Min plan required
Used by agents
Automation platform
Workflow orchestration across all three agents
Internal credential store; per-tool OAuth or API key
Standard/paid tier with webhook ingestion and scheduled triggers
All agents
HubSpot
Subscriber data source and status sink
OAuth 2.0 (private app token)
Starter CRM or above (API access required)
Agent 1, Agent 2
Xero
Invoice creation, payment marking, and reconciliation
OAuth 2.0 (authorization code flow)
Starter or above (Invoicing feature enabled)
Agent 1, Agent 3
Stripe
Payment charging, failure detection, and retry scheduling
API key (secret key) + webhook signing secret
Standard Stripe account (no minimum plan; per-transaction fees apply)
Agent 1, Agent 2, Agent 3
Gmail
Invoice confirmation and failed-payment notification emails
OAuth 2.0 (Google Workspace service account or user OAuth)
Google Workspace Business Starter or personal Gmail with OAuth consent screen
Agent 2, Agent 3
Slack
Billing run summary posted to finance channel
OAuth 2.0 (Slack app with bot token)
Free tier or above (incoming webhooks or bot API)
Agent 3
Before you connect anything: all integrations must be authenticated and smoke-tested against sandbox or test environments before any production credentials are entered. For Stripe, use the test-mode API keys and generate test webhook events. For Xero, use the Xero developer demo company. For HubSpot, use a sandbox portal. Only after FullSpec confirms each sandbox connection is stable should production credentials be substituted.

02Per-tool integration detail

HubSpot

HubSpot is the subscriber data source for Agent 1 (Billing Cycle Agent) and the status-update sink for Agent 2 (Failed Payment Recovery Agent). The automation reads contact and deal records to build the billing list and writes billing-status properties back after each payment outcome.

Auth method
OAuth 2.0 via HubSpot Private App. Generate a private app token in Settings > Integrations > Private Apps. Store the token in the credential store as HUBSPOT_API_TOKEN. Token does not expire unless revoked; rotation should be scheduled every 90 days.
Required scopes
crm.objects.contacts.read, crm.objects.contacts.write, crm.objects.deals.read, crm.objects.deals.write, crm.schemas.contacts.read
Trigger / polling
Polled on schedule (no inbound webhook from HubSpot for this agent). Agent 1 polls the Contacts API at billing cycle start using a filter on the custom property subscription_status = active. Poll interval is one-off at cycle trigger, not continuous.
Required configuration
Custom contact properties that must exist before go-live: subscription_status (enumeration: active, suspended, cancelled), subscription_tier (enumeration matching Xero account codes), billing_cycle_day (number), mrr_amount (number, USD), last_payment_status (enumeration: paid, failed, pending), last_payment_date (date). Property internal names must match exactly as listed. Do not hardcode property IDs; reference by internal name via the API.
Rate limits
HubSpot enforces 100 requests per 10 seconds and 40,000 requests per day on the Standard tier. At 120 subscribers per cycle, Agent 1 makes approximately 122 API calls per billing run (1 list fetch + 1 record update per subscriber). Agent 2 makes up to 240 status-update calls per month across retry events. Total monthly API volume is well under 1,000 calls. No throttling logic is required at current volume, but implement a 200 ms inter-request delay as a precaution.
Constraints
Private app tokens are scoped to a single HubSpot portal. If the business operates multiple portals, a separate credential entry is required per portal. The automation must not create new contacts; it reads and updates only. Association between contact and deal records must be confirmed in the portal before build.
// Input to Agent 1
GET /crm/v3/objects/contacts?filterGroups=[{subscription_status=active}]
  Returns: contact_id, email, firstname, lastname, subscription_tier,
           mrr_amount, billing_cycle_day, stripe_customer_id

// Output from Agent 2
PATCH /crm/v3/objects/contacts/{contact_id}
  Body: { last_payment_status, last_payment_date, subscription_status }
Xero

Xero is used by Agent 1 (Billing Cycle Agent) to create invoices and by Agent 3 (Reconciliation and Notification Agent) to mark invoices as paid and flag unmatched items for human review.

Auth method
OAuth 2.0 authorization code flow. Register a Xero app in the Xero Developer Portal, obtain client_id and client_secret, and complete the consent flow to obtain an access token and refresh token. Store as XERO_CLIENT_ID, XERO_CLIENT_SECRET, XERO_REFRESH_TOKEN, and XERO_TENANT_ID in the credential store. Access tokens expire after 30 minutes; the automation platform must implement silent refresh using the refresh token before each API call session.
Required scopes
openid, profile, email, accounting.transactions, accounting.transactions.read, accounting.contacts, accounting.contacts.read, accounting.settings.read, offline_access
Trigger / webhook
No inbound Xero webhook is used. Agent 1 posts invoices via API on trigger. Agent 3 polls Xero for invoice status changes after receiving a Stripe payment-succeeded event, then patches the invoice record.
Required configuration
The following must be confirmed and stored before build: XERO_ACCOUNT_CODE_MONTHLY (e.g. 200) mapped to monthly subscription line items; XERO_ACCOUNT_CODE_ANNUAL mapped to annual plan line items; XERO_TAX_TYPE per plan tier (e.g. TAX001 for standard rate); XERO_BRANDING_THEME_ID for invoice templates. All IDs stored in credential store, not hardcoded. Invoice line item description must use the template: '{subscription_tier} Subscription - {billing_month_year}'.
Rate limits
Xero enforces 60 API calls per minute and a daily limit of 5,000 calls. Agent 1 makes up to 121 calls per billing run (1 tenant verification + 120 invoice creations). Agent 3 makes up to 120 PATCH calls per reconciliation pass. Peak combined load is approximately 241 calls per billing run, well within the per-minute limit if invoice creation is batched with a 1-second delay between calls. No throttling middleware is required, but a retry-after header must be respected if a 429 is returned.
Constraints
Xero invoices must have status AUTHORISED before they can be marked as paid. Agent 1 must set InvoiceStatus to AUTHORISED, not DRAFT, on creation. Voiding or deleting invoices via API requires the accounting.transactions scope and must only be performed by the exception-handling flow, not the main billing path. Currency must be confirmed as a single currency (USD) before build; multi-currency requires the Xero Premium plan.
// Input to Agent 1 (invoice creation)
POST /api.xro/2.0/Invoices
  Body per subscriber: { Type: ACCREC, Contact: { ContactID },
    LineItems: [{ Description, Quantity: 1, UnitAmount: mrr_amount,
    AccountCode, TaxType }], InvoiceStatus: AUTHORISED,
    DueDate: billing_cycle_day + 7 days, Reference: stripe_payment_intent_id }

// Output from Agent 3 (mark as paid)
POST /api.xro/2.0/Payments
  Body: { Invoice: { InvoiceID }, Account: { Code: XERO_BANK_ACCOUNT_CODE },
    Date: payment_date, Amount: amount_paid }
Stripe

Stripe is used by Agent 1 to trigger payment charges, by Agent 2 to detect failures and schedule retries, and by Agent 3 to confirm successful payments for reconciliation. Stripe is the event source for both the failure and success branches of the automation.

Auth method
Secret API key for API calls (stored as STRIPE_SECRET_KEY). Webhook signing secret for inbound event validation (stored as STRIPE_WEBHOOK_SIGNING_SECRET). Restricted keys are strongly preferred over the full secret key: create a restricted key with read/write on PaymentIntents, Customers, and Charges only. Never expose the secret key in logs or error messages.
Required scopes (restricted key permissions)
PaymentIntents: read, write; Charges: read; Customers: read; Webhooks: read (for testing only, remove in production)
Webhook setup
Register a webhook endpoint in the Stripe Dashboard pointing to the automation platform's inbound URL. Subscribe to the following events: payment_intent.succeeded, payment_intent.payment_failed, charge.failed, charge.dispute.created. All inbound webhook payloads must be validated using the STRIPE_WEBHOOK_SIGNING_SECRET via HMAC-SHA256 signature check before processing. Reject any payload that fails signature validation with a 400 response.
Required configuration
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SIGNING_SECRET, and STRIPE_RETRY_DELAY_HOURS (default: 72) stored in credential store. The stripe_customer_id for each subscriber must be populated in HubSpot before Agent 1 runs. Payment method must already be attached to the Stripe Customer object; the automation does not collect new card details.
Rate limits
Stripe enforces 100 read and 100 write requests per second in live mode. At 120 subscribers, Agent 1 makes 120 PaymentIntent creation calls per billing run. With a 500 ms inter-request delay, the full batch completes in approximately 60 seconds, well within rate limits. No throttling middleware is required at current volume.
Constraints
Stripe's own retry rules (Smart Retries or manual) must not conflict with the automation's 72-hour retry schedule. Disable Stripe Smart Retries for the customer segment managed by this automation, or the automation will double-retry. Disputed charges (charge.dispute.created) must be routed to a manual exception queue and must not be retried automatically. Chargebacks are out of scope for the automation.
// Input to Agent 1 (charge subscriber)
POST /v1/payment_intents
  Body: { amount: mrr_amount_cents, currency: 'usd',
    customer: stripe_customer_id, payment_method: default_payment_method,
    confirm: true, metadata: { xero_invoice_id, hubspot_contact_id } }

// Inbound webhook to Agent 2 (failure event)
POST {platform_inbound_url}/stripe/webhook
  Stripe-Signature header validated against STRIPE_WEBHOOK_SIGNING_SECRET
  Event type: payment_intent.payment_failed
  Payload fields used: id, customer, amount, last_payment_error.message,
    metadata.xero_invoice_id, metadata.hubspot_contact_id

// Inbound webhook to Agent 3 (success event)
  Event type: payment_intent.succeeded
  Payload fields used: id, customer, amount, created,
    metadata.xero_invoice_id, metadata.hubspot_contact_id
Integration and API SpecPage 1 of 3
FS-DOC-05Technical
Gmail

Gmail is used by Agent 2 (Failed Payment Recovery Agent) to send payment-failure notifications and by Agent 3 (Reconciliation and Notification Agent) to send invoice confirmation emails to successful payers. All emails use pre-approved templates stored in the credential or configuration store.

Auth method
OAuth 2.0 using a Google Workspace service account with domain-wide delegation, or a named user OAuth grant. Store credentials as GMAIL_OAUTH_CLIENT_ID, GMAIL_OAUTH_CLIENT_SECRET, GMAIL_OAUTH_REFRESH_TOKEN, and GMAIL_SENDER_ADDRESS. The sender address must match the authenticated account. Refresh tokens do not expire unless access is revoked; monitor for revocation events.
Required scopes
https://www.googleapis.com/auth/gmail.send
Trigger
No inbound Gmail trigger. Gmail is an output-only integration for this process. Emails are composed and sent by the automation in response to Stripe events.
Required configuration
Two email templates must be authored and approved before build: TEMPLATE_PAYMENT_FAILED (includes subscriber first name, invoice amount, card update link via Stripe Customer Portal URL) and TEMPLATE_PAYMENT_CONFIRMED (includes subscriber first name, invoice amount, Xero invoice PDF link). Template placeholders: {{first_name}}, {{invoice_amount}}, {{invoice_id}}, {{card_update_url}}, {{invoice_pdf_url}}. Templates stored as plain-text with HTML alternative in the configuration store, referenced by key. Do not hardcode template content in workflow steps.
Rate limits
Gmail API enforces 250 quota units per second and 1,000,000 quota units per day. Sending one email costs 100 quota units. At 120 subscribers per billing run, the batch of confirmation emails consumes 12,000 quota units, well within daily limits. A 300 ms inter-send delay is sufficient to avoid per-second quota breaches.
Constraints
The Gmail API send method does not support scheduled delivery; the automation platform must manage send timing. Emails must include an unsubscribe header (List-Unsubscribe) to comply with Google's bulk sender policy if the sender domain sends more than 5,000 messages per day. At current volume this threshold is not reached, but the header should be included regardless. Bounce handling is out of scope for this automation.
// Output from Agent 2 (failed payment email)
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
  Body (base64-encoded RFC 2822): To: subscriber_email,
    From: GMAIL_SENDER_ADDRESS, Subject: 'Action required: payment failed',
    Body: TEMPLATE_PAYMENT_FAILED rendered with { first_name, invoice_amount,
    card_update_url }

// Output from Agent 3 (confirmation email)
  Subject: 'Payment confirmed - your invoice is attached',
  Body: TEMPLATE_PAYMENT_CONFIRMED rendered with { first_name,
    invoice_amount, invoice_pdf_url }
Slack

Slack is used exclusively by Agent 3 (Reconciliation and Notification Agent) to post a billing run summary to the designated finance channel at the end of each billing cycle. This is the only outbound message; Slack sends no inbound events to the automation.

Auth method
OAuth 2.0 Slack app with bot token. Install the app into the workspace, grant the required scopes, and store the bot token as SLACK_BOT_TOKEN. Alternatively, use an incoming webhook URL stored as SLACK_WEBHOOK_URL. Bot token is preferred for flexibility. Token does not expire unless the app is uninstalled or revoked.
Required scopes
chat:write, chat:write.public (if posting to channels the bot has not joined), incoming-webhook (if using webhook method instead of bot token)
Webhook / channel setup
The finance channel ID must be confirmed and stored as SLACK_FINANCE_CHANNEL_ID. The bot must be invited to the channel (/invite @botname) before the first run. Do not hardcode the channel name; use the channel ID to avoid breakage if the channel is renamed.
Required configuration
SLACK_BOT_TOKEN and SLACK_FINANCE_CHANNEL_ID stored in credential store. Message format uses Slack Block Kit with sections for: total invoices raised, total collected (USD), count of failed payments, count of retries scheduled, and count of unmatched reconciliation items requiring human review. Block template stored in configuration store as SLACK_BILLING_SUMMARY_TEMPLATE.
Rate limits
Slack enforces 1 message per second per channel for the chat.postMessage method and a burst limit of approximately 20 requests per minute per workspace. Agent 3 posts one message per billing run. No throttling is required.
Constraints
Slack messages are append-only; the automation cannot edit or delete a posted summary. If a billing run is rerun due to an error, a second summary will appear in the channel. Include the billing period date in the message header to avoid confusion. Rich text formatting must use Block Kit JSON; legacy attachment fields are deprecated.
// Output from Agent 3
POST https://slack.com/api/chat.postMessage
  Headers: Authorization: Bearer SLACK_BOT_TOKEN
  Body: { channel: SLACK_FINANCE_CHANNEL_ID, blocks: [
    { type: 'header', text: 'Billing Run Summary - {billing_period}' },
    { type: 'section', fields: [
      { text: 'Invoices raised: {total_invoices}' },
      { text: 'Collected: ${total_collected}' },
      { text: 'Failed: {failed_count}' },
      { text: 'Retries scheduled: {retry_count}' },
      { text: 'Unmatched items: {unmatched_count}' } ] } ] }

03Field mappings between tools

The tables below define the exact field mappings for each agent handoff. Source and destination field names are given in monospace format exactly as they appear in the respective API responses and request bodies. Any mismatch in field names between the automation platform mapping configuration and these names will cause a silent data error.

Handoff 1: HubSpot to Xero (Agent 1, Billing Cycle Agent)

Source tool
Source field
Destination tool
Destination field
HubSpot
`properties.email`
Xero
`Invoices[].Contact.EmailAddress`
HubSpot
`properties.firstname` + `properties.lastname`
Xero
`Invoices[].Contact.Name`
HubSpot
`properties.mrr_amount`
Xero
`Invoices[].LineItems[0].UnitAmount`
HubSpot
`properties.subscription_tier`
Xero
`Invoices[].LineItems[0].AccountCode` (via tier-to-code map)
HubSpot
`properties.billing_cycle_day`
Xero
`Invoices[].DueDate` (cycle day + 7 days)
HubSpot
`properties.hubspot_contact_id`
Stripe
`metadata.hubspot_contact_id`
Xero (response)
`Invoices[].InvoiceID`
Stripe
`metadata.xero_invoice_id`

Handoff 2: Stripe to HubSpot (Agent 2, Failed Payment Recovery Agent)

Source tool
Source field
Destination tool
Destination field
Stripe
`data.object.metadata.hubspot_contact_id`
HubSpot
`properties.last_payment_status` = 'failed'
Stripe
`data.object.created` (Unix timestamp)
HubSpot
`properties.last_payment_date` (ISO 8601)
Stripe
`data.object.last_payment_error.message`
HubSpot
`properties.last_payment_error_message`
Stripe
`data.object.customer`
HubSpot
`properties.stripe_customer_id` (read-only, for lookup)
Stripe
`data.object.amount`
Gmail
Template variable `{{invoice_amount}}` (divide by 100 for USD)

Handoff 3: Stripe to Xero (Agent 3, Reconciliation and Notification Agent)

Source tool
Source field
Destination tool
Destination field
Stripe
`data.object.metadata.xero_invoice_id`
Xero
`Payments[].Invoice.InvoiceID`
Stripe
`data.object.amount`
Xero
`Payments[].Amount` (divide by 100 for USD)
Stripe
`data.object.created` (Unix timestamp)
Xero
`Payments[].Date` (ISO 8601 yyyy-MM-dd)
Stripe
`data.object.id`
Xero
`Payments[].Reference`
Stripe (response)
`data.object.metadata.hubspot_contact_id`
HubSpot
`properties.last_payment_status` = 'paid'

Handoff 4: Xero to Gmail (Agent 3, invoice confirmation email)

Source tool
Source field
Destination tool
Destination field
Xero (invoice response)
`Invoices[].InvoiceID`
Gmail
Template variable `{{invoice_id}}`
Xero (invoice response)
`Invoices[].Url`
Gmail
Template variable `{{invoice_pdf_url}}`
Xero (invoice response)
`Invoices[].AmountDue`
Gmail
Template variable `{{invoice_amount}}`
HubSpot (via contact lookup)
`properties.firstname`
Gmail
Template variable `{{first_name}}`
Stripe
`data.object.receipt_url`
Gmail
Template variable `{{payment_receipt_url}}`
Integration and API SpecPage 2 of 3
FS-DOC-05Technical

04Build stack and orchestration

Orchestration layout
Three discrete workflows, one per agent. Each workflow is independently deployable and testable. Workflows share a single credential store; no credentials are duplicated across workflows. Shared utility functions (timestamp conversion, amount unit conversion, template rendering) are maintained as reusable modules called by all three workflows.
Agent 1 trigger mechanism
Scheduled trigger. The Billing Cycle Agent runs on a date-based schedule, evaluated daily. The workflow checks HubSpot for contacts where billing_cycle_day matches the current calendar day. If one or more matches are found, the billing run executes. No inbound webhook. Timezone must be set to the business's local timezone (confirm at onboarding); all date comparisons must be timezone-aware.
Agent 2 trigger mechanism
Inbound webhook (Stripe event: payment_intent.payment_failed and charge.failed). The automation platform exposes an HTTPS endpoint. Every inbound Stripe payload is validated via HMAC-SHA256 signature check against STRIPE_WEBHOOK_SIGNING_SECRET before any processing begins. Payloads that fail validation are rejected with HTTP 400 and logged to the error queue. No polling fallback for this agent.
Agent 2 retry scheduler
After the initial failure notification email, Agent 2 queues a delayed execution step set to fire after STRIPE_RETRY_DELAY_HOURS (default 72 hours). The delayed step re-attempts the Stripe PaymentIntent and branches on the outcome. A maximum of two automated retries is enforced. After two failures, the subscriber record is updated to subscription_status = suspended in HubSpot and the case is added to the manual exception queue.
Agent 3 trigger mechanism
Inbound webhook (Stripe event: payment_intent.succeeded). Same signature validation as Agent 2. Agent 3 also receives the output of Agent 2's successful retry path via an internal workflow trigger (not a Stripe webhook) to ensure reconciliation runs for both first-attempt and retried payments.
Credential store structure
All secrets are stored in the automation platform's native encrypted credential store. No secret is written into a workflow step, code block, or log output. See the code block below for the full credential inventory.
Logging and observability
Each workflow step writes a structured log entry: timestamp, agent name, step name, input payload hash (not raw payload), outcome (success/failure), and error message if applicable. Logs are retained for 30 days minimum. Failed runs trigger an alert to SLACK_FINANCE_CHANNEL_ID with the error details and the affected subscriber's hubspot_contact_id.
Credential store inventory. All values populated during the Discovery and Data Mapping stage and validated against sandbox before production cutover.
// Credential store contents (all entries encrypted at rest)
// Do NOT hardcode any of these values in workflow steps

// HubSpot
HUBSPOT_API_TOKEN            = <private app token>
HUBSPOT_PORTAL_ID            = <portal numeric ID>

// Xero
XERO_CLIENT_ID               = <OAuth app client ID>
XERO_CLIENT_SECRET           = <OAuth app client secret>
XERO_REFRESH_TOKEN           = <OAuth refresh token, auto-rotated>
XERO_TENANT_ID               = <Xero organisation tenant ID>
XERO_ACCOUNT_CODE_MONTHLY    = <e.g. 200>
XERO_ACCOUNT_CODE_ANNUAL     = <e.g. 201>
XERO_TAX_TYPE                = <e.g. TAX001>
XERO_BRANDING_THEME_ID       = <invoice template UUID>
XERO_BANK_ACCOUNT_CODE       = <reconciliation bank account code>

// Stripe
STRIPE_SECRET_KEY            = <restricted key, PaymentIntents + Charges>
STRIPE_WEBHOOK_SIGNING_SECRET = <whsec_...>
STRIPE_RETRY_DELAY_HOURS     = 72
STRIPE_CUSTOMER_PORTAL_URL   = <https://billing.stripe.com/p/login/...>

// Gmail
GMAIL_OAUTH_CLIENT_ID        = <Google OAuth client ID>
GMAIL_OAUTH_CLIENT_SECRET    = <Google OAuth client secret>
GMAIL_OAUTH_REFRESH_TOKEN    = <OAuth refresh token>
GMAIL_SENDER_ADDRESS         = <billing@yourdomain.com>
TEMPLATE_PAYMENT_FAILED      = <key referencing email template store>
TEMPLATE_PAYMENT_CONFIRMED   = <key referencing email template store>

// Slack
SLACK_BOT_TOKEN              = <xoxb-...>
SLACK_FINANCE_CHANNEL_ID     = <C0XXXXXXXXX>
SLACK_BILLING_SUMMARY_TEMPLATE = <key referencing Block Kit template store>
The Xero refresh token is the most operationally sensitive credential in this stack. Xero refresh tokens expire if unused for 60 days. The automation platform must implement automatic silent refresh on every Xero API session. FullSpec configures a daily no-op Xero health-check call to prevent token expiry during low-activity periods. If the token is revoked or expires, the Billing Cycle Agent will fail at the invoice creation step; this failure must trigger an immediate alert to SLACK_FINANCE_CHANNEL_ID.

05Error handling and retry logic

Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. All errors are logged to the structured log and, where they affect a subscriber's billing status, an alert is posted to SLACK_FINANCE_CHANNEL_ID with sufficient context for a human to act. The table below covers all integration-level failure scenarios.

Integration
Scenario
Required behaviour
HubSpot (Agent 1)
Subscriber list fetch returns zero records
Halt billing run. Post alert to Slack: 'Billing run aborted: zero active subscribers returned from HubSpot. Verify filter and retry manually.' Do not proceed to Xero invoice creation.
HubSpot (Agent 1 / Agent 2)
PATCH to update contact property returns 4xx
Retry up to 3 times with exponential backoff (5 s, 15 s, 45 s). If all retries fail, log the contact_id and failed property update to the exception queue and continue processing remaining subscribers. Do not block the full billing run.
Xero (Agent 1)
Invoice creation returns 409 Conflict (duplicate reference)
Check whether the invoice already exists using GET /Invoices?where=Reference='stripe_payment_intent_id'. If found and AUTHORISED, skip creation and proceed with the existing InvoiceID. If not found, log as unresolved conflict and add to manual exception queue.
Xero (Agent 1)
Invoice creation returns 401 or 403 (auth failure)
Attempt one silent token refresh and retry the request. If the refresh fails, halt the billing run, log the error with timestamp, and post an urgent alert to Slack: 'Xero authentication failure. Billing run halted. Manual intervention required.'
Xero (Agent 3)
Payment mark fails because invoice status is not AUTHORISED
Log the InvoiceID and current status. Add to the unmatched reconciliation queue for human review. Post a line item in the Slack billing summary: 'Unmatched invoice: {InvoiceID} - status {current_status}.' Do not retry automatically.
Stripe (Agent 1)
PaymentIntent creation returns 402 (card declined)
This is an expected business outcome, not a system error. Record the failure against the subscriber, trigger Agent 2 via internal workflow event, and continue to the next subscriber. Do not treat as an exception.
Stripe (Agent 1)
PaymentIntent creation returns 5xx (Stripe server error)
Retry up to 3 times with exponential backoff (10 s, 30 s, 90 s). If all retries fail, log the subscriber and invoice as unprocessed, add to manual exception queue, and post an alert to Slack. Do not mark the Xero invoice as paid.
Stripe (Agent 2)
Inbound webhook signature validation fails
Reject the request immediately with HTTP 400. Log the raw Stripe-Signature header value and the timestamp. Do not process the payload. If more than 3 consecutive validation failures occur within 10 minutes, post a security alert to Slack and pause webhook processing until manually cleared.
Stripe (Agent 2)
Retry charge fails for second time (max retries reached)
Update HubSpot: subscription_status = suspended. Send a final escalation email to subscriber via Gmail using a separate TEMPLATE_PAYMENT_ESCALATION template. Log the case. Do not attempt further automated charges. Add to manual review queue.
Gmail (Agent 2 / Agent 3)
Email send returns 429 (quota exceeded)
Respect the Retry-After header. Pause the send queue and resume after the indicated wait period. If the quota is not restored within 15 minutes, log all unsent emails with subscriber details to the exception queue and post an alert to Slack. No email is silently dropped.
Gmail (Agent 2 / Agent 3)
Email send returns 401 (OAuth token expired or revoked)
Attempt one silent token refresh using GMAIL_OAUTH_REFRESH_TOKEN. If refresh succeeds, retry the send. If refresh fails, halt email sending for the current run, log all unsent emails, and post an urgent alert to Slack: 'Gmail authentication failure. Email notifications paused.'
Slack (Agent 3)
Billing summary post fails (any error)
Retry once after 30 seconds. If the second attempt fails, log the full summary payload to the structured log. Do not treat a Slack failure as a critical error; the billing run data is already committed in Xero and HubSpot. Email a plain-text fallback summary to GMAIL_SENDER_ADDRESS as a backup notification.
Unhandled exceptions must never fail silently. Any error path not explicitly covered by the table above must default to: log the full error payload with a timestamp, add the affected record to the manual exception queue, and post a Slack alert. The exception queue is reviewed by FullSpec during the first 30 days post-go-live and ownership is then handed to your team's designated billing admin. Contact support@gofullspec.com for any integration failure that is not resolved by the retry logic described above.
Integration and API SpecPage 3 of 3

More documents for this process

Every document generated for Subscription & Recurring Billing.

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