Back to Renewal & Contract Expiry Management

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Renewal and Contract Expiry Management

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

This document gives the FullSpec build team everything needed to construct, configure, and hand over the three-agent renewal automation. It covers the current-state process with bottleneck flags, full agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The process owner's responsibilities are limited to providing API credentials, confirming HubSpot field names, and signing off on UAT. All build, test, and deployment work is handled by FullSpec.

01Process snapshot

Step
Name
Description
01 BOTTLENECK
Pull Weekly Contract Expiry Report
Account Manager manually opens the contract spreadsheet and filters for contracts expiring in the next 90 days. Data is frequently outdated or incomplete, extending the task. Time cost: 30 min/cycle.
02
Cross-Reference CRM for Account Status
Account Manager checks HubSpot to confirm each flagged contract is still active and reviews communication history. Inconsistencies between spreadsheet and CRM require manual reconciliation. Time cost: 25 min/cycle.
03
Prioritise Renewal List by Value and Risk
Account Manager manually sorts contracts by deal value, relationship strength, and expiry urgency. The prioritisation is informal and rarely documented. Time cost: 20 min/cycle.
04 BOTTLENECK
Draft and Send Initial Renewal Outreach Email
A personalised renewal email is written from scratch or copied from a prior email and sent via Gmail. No standard template exists, so tone and content vary across account managers. Time cost: 45 min/cycle.
05
Log Outreach Activity in CRM
Account Manager manually creates a CRM activity record in HubSpot after sending the email. Frequently skipped under time pressure. Time cost: 10 min/cycle.
06
Chase Non-Responders at 60 and 30 Days
Follow-up email or call is made at the 60-day and 30-day marks if the client has not responded. Entirely dependent on the account manager remembering to check their own notes. Time cost: 30 min/cycle.
07
Prepare Renewal Proposal or Contract Document
Once the client confirms interest, a renewal proposal is drafted manually or by duplicating the prior contract and updating terms. Pricing checks with finance add delays. Time cost: 40 min/cycle.
08 BOTTLENECK
Route Proposal for Internal Approval
Draft renewal is shared over Slack or email with a sales manager or finance lead for sign-off. Approvals are untracked and can stall for days with no chase mechanism. Time cost: 35 min/cycle.
09
Send Contract for Client Signature
Once approved, the contract document is manually uploaded to DocuSign and sent to the client for e-signature. Client reminders are sent ad hoc. Time cost: 15 min/cycle.
10
Update CRM and Billing on Signed Contract
Account Manager updates the HubSpot deal record with new contract dates and manually raises a renewal invoice in Xero. Errors in dates or amounts occur regularly. Time cost: 20 min/cycle.
Time cost summary: Total manual time per renewal cycle is 270 minutes (4.5 hours). At 30 to 50 renewals per month the team carries 26 hours of renewal admin every 30 days, equivalent to 6 hours per week. Steps 1, 2, 3, and 5 are fully replaced by the Contract Monitoring Agent. Steps 4 and 6 are fully replaced by the Renewal Outreach Agent. Steps 8, 9, and 10 are fully replaced by the Approval and Closing Agent. Step 7 (custom proposal preparation) remains manual for non-standard contracts. Step 8's approval routing is automated but requires a human decision inside Slack.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Contract Monitoring Agent

Runs on a daily 8am schedule against HubSpot deal records to identify all active contracts whose expiry date falls within a 90, 60, or 30-day window and where no renewal activity has been logged. It enriches each flagged record with deal value, primary contact details, and last activity date, then scores and ranks the list by days remaining and deal value. The ranked output is written back to HubSpot as a custom property update and mirrored to a Google Sheet for audit purposes. Estimated build time: 10 hours. Complexity: Moderate.

Trigger
Scheduled cron at 08:00 daily (owner's local timezone); also fires on HubSpot deal record update via webhook when the expiry_date or renewal_status field changes.
Tools
HubSpot (CRM read and write), Google Sheets (audit log write)
Replaces steps
Steps 1, 2, 3, and 5
Estimated build
10 hours, Moderate
// Input
HubSpot deal query: dealstage = 'active', expiry_date <= TODAY + 90 days
Fields pulled: deal_id, deal_name, expiry_date, deal_value, owner_id,
               contact_id, last_activity_date, renewal_status

// Processing
Score = (deal_value * 0.6) + (urgency_weight[days_remaining] * 0.4)
urgency_weight: 0-30 days = 3, 31-60 days = 2, 61-90 days = 1
Filter: exclude deals where renewal_status = 'outreach_active' or 'closed_won'

// Output
HubSpot deal property update: renewal_priority_score, renewal_queue_date
Google Sheet row append: deal_id, deal_name, expiry_date, deal_value,
                         owner_email, priority_score, queue_timestamp
Payload to Renewal Outreach Agent: ranked_deal_list[]
  • HubSpot tier must be Sales Hub Starter or above to access deal property write via API. Confirm the connected account has the oauth scope crm.objects.deals.write before build.
  • The expiry_date field must be a HubSpot date property (not a text field). The data audit must confirm this field is populated for all active deals before the agent can run. A one-off migration script may be required.
  • The Google Sheet must be pre-created with a named tab 'Renewal Queue' and the header row matching the output fields exactly. The sheet ID is stored in the shared credential store.
  • Dedupe logic: if a deal already has renewal_queue_date set within the current calendar day, skip the write to avoid duplicate Slack notifications downstream.
  • Fallback: if HubSpot API returns a 429 rate-limit response, retry after 60 seconds up to three times, then log the failure to the error table and send a Slack alert to the build team channel.
  • Confirm with the process owner which HubSpot pipeline and stages map to 'active contract' before coding the deal query filter.
Renewal Outreach Agent

Receives the ranked deal list from the Contract Monitoring Agent and sends a personalised renewal email from the assigned account manager's connected Gmail account at each of the 90, 60, and 30-day marks. It monitors inbound Gmail threads for client replies using label-based detection and pauses the sequence automatically when a reply is detected on a tracked thread. Each sent and scheduled email is logged as a HubSpot activity on the corresponding deal record with timestamp and sequence stage. Estimated build time: 12 hours. Complexity: Moderate.

Trigger
Activated by payload from Contract Monitoring Agent containing ranked_deal_list[]; also re-triggered by the daily schedule for 60-day and 30-day follow-up checks.
Tools
Gmail (send via account manager's connected account, reply detection via label polling), HubSpot (activity log write, renewal_status update)
Replaces steps
Steps 4 and 6
Estimated build
12 hours, Moderate
// Input
ranked_deal_list[]: { deal_id, deal_name, expiry_date, deal_value,
                       contact_email, contact_first_name, owner_email,
                       priority_score, sequence_stage }
sequence_stage: '90_day' | '60_day' | '30_day'

// Processing
Select email template by sequence_stage
Populate template variables: {contact_first_name}, {deal_name},
                             {expiry_date}, {account_manager_name}
Send via Gmail API using owner OAuth token (per-account-manager credential)
Store thread_id against deal_id in tracking table
Poll Gmail label 'renewal_replied' every 15 min; if thread_id matched, set
  HubSpot renewal_status = 'client_replied', pause sequence for deal

// Output
Gmail: sent email from owner_email to contact_email
HubSpot activity log: { deal_id, activity_type: 'email_sent',
                        sequence_stage, timestamp, gmail_thread_id }
HubSpot deal property update: renewal_status, last_outreach_date
Payload to Approval and Closing Agent (on reply detection or manual trigger):
  { deal_id, deal_name, deal_value, contact_email, owner_email,
    sequence_stage, thread_id }
  • Each account manager must individually authorise the Gmail OAuth connection using their own Google account. The required OAuth scopes are: gmail.send, gmail.readonly, gmail.labels, gmail.modify. A shared inbox or alias connection requires a separate service account flow and must be confirmed before build.
  • Three email templates must be provided by the process owner before build starts: 90-day initial outreach, 60-day follow-up, and 30-day final notice. Templates must include the merge field placeholders listed in the IO block.
  • Reply detection uses a Gmail label named 'renewal_replied' applied by an automated Gmail filter rule. This filter must be created in the connected Gmail account as part of build setup.
  • If the team uses multiple account managers, each will need a separate OAuth credential entry in the shared credential store keyed by HubSpot owner_id.
  • Fallback: if a Gmail send fails (token expired or rate limit), log to the error table, flag the deal in HubSpot with renewal_status = 'outreach_failed', and send a Slack alert to the account manager.
  • Confirm whether BCC or blind logging to a shared inbox is required for compliance before configuring the send step.
Approval and Closing Agent

Manages the internal approval step, DocuSign contract delivery, and all post-signature write-backs. When a deal is flagged as ready for approval (either by the account manager via a HubSpot property update or by clicking the action button in the Slack summary message), the agent posts a structured Slack approval request to the sales manager's channel. On approval, it generates a DocuSign envelope from the standard renewal template pre-filled with deal terms, sends it to the client contact, and waits for a signed callback. On signature completion it updates the HubSpot deal stage to Renewed, writes the new contract start and end dates, and raises a recurring invoice in Xero. Estimated build time: 14 hours. Complexity: Complex.

Trigger
HubSpot deal property approval_requested = true (set manually by account manager or via Slack action); or Renewal Outreach Agent payload on client reply confirmation.
Tools
Slack (approval request and action handling), DocuSign (envelope creation and signed webhook), HubSpot (deal stage and property write), Xero (invoice creation)
Replaces steps
Steps 7 (standard contracts only), 8, 9, and 10
Estimated build
14 hours, Complex
// Input
{ deal_id, deal_name, deal_value, contract_start_date, contract_end_date,
  contact_email, contact_name, account_manager_name, owner_email,
  is_standard_terms: true | false }

// Branch: non-standard terms
IF is_standard_terms = false:
  Set HubSpot renewal_status = 'manual_proposal_required'
  Send Slack alert to owner_email: 'Custom proposal needed for {deal_name}'
  STOP (account manager prepares proposal manually, then re-triggers)

// On standard terms: Slack approval request
Slack message to sales_manager_channel:
  { deal_name, deal_value, contract_end_date, account_manager_name,
    action_buttons: ['Approve', 'Decline'] }
Await Slack interactive payload (timeout: 48 hours)

// On approval
DocuSign envelope: template_id (renewal_standard), prefill fields:
  { client_name: contact_name, deal_value, contract_start_date,
    contract_end_date, account_manager_name }
Send envelope to contact_email; cc owner_email
Store envelope_id against deal_id in tracking table

// On DocuSign signed webhook callback
HubSpot deal update: { dealstage: 'closed_won_renewed',
                        contract_start_date, contract_end_date,
                        renewal_status: 'signed' }
Xero invoice create: { contact_name, line_item: 'Contract Renewal',
                        amount: deal_value, due_date: contract_start_date,
                        invoice_type: 'ACCREC', status: 'DRAFT' }

// Output
Slack confirmation to owner_email: 'Contract signed and invoice raised for {deal_name}'
HubSpot deal: updated stage, dates, renewal_status = 'signed'
Xero: draft invoice created (InvoiceID stored on HubSpot deal as custom property)
  • DocuSign integration requires an account at DocuSign Business Pro or above for template-based envelope creation via API. Confirm the connected DocuSign account has at least one published renewal template before build begins. The template_id must be stored in the credential store.
  • The Slack approval message must use Slack Block Kit with interactive buttons. The Slack app must have the scopes chat:write, commands, and interactivity enabled. The app's request URL for interactive payloads must point to the automation platform's public webhook endpoint.
  • Slack approval timeout is set at 48 hours. If no action is taken, the agent re-posts a reminder to the sales manager channel and logs the stall to the error table. After 72 hours with no response, it escalates to the HubSpot deal owner via email.
  • Xero invoice creation uses the Xero Accounting API (OAuth 2.0). The connected Xero organisation must have a contact record matching the HubSpot contact name or email before the invoice step fires. Mismatched contact names will require a fuzzy-match lookup step in the flow.
  • The is_standard_terms flag must be set as a HubSpot deal property (boolean). Confirm the criteria that define a non-standard contract with the process owner before coding the branch logic.
  • DocuSign signed status is delivered via a Connect webhook (event: envelope_completed). The automation platform endpoint for this webhook must be registered in the DocuSign admin console and secured with HMAC verification.
  • Xero invoices are created in DRAFT status. Confirm with the process owner whether auto-approval to SUBMITTED or AUTHORISED status is acceptable, or whether a finance team member must approve before sending.
Build time breakdown: Contract Monitoring Agent 10 hours, Renewal Outreach Agent 12 hours, Approval and Closing Agent 14 hours. End-to-end integration testing and error handling adds a further 2 hours. Total confirmed build effort: 38 hours, matching the snapshot figure.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with agent handoff boundaries and field names
// ============================================================
// TRIGGER
// ============================================================
SCHEDULE: daily cron 08:00 (owner timezone)
  OR
WEBHOOK: HubSpot deal.properties.expiry_date updated
         HubSpot deal.properties.renewal_status updated

// ============================================================
// AGENT 1: Contract Monitoring Agent
// ============================================================
-> HubSpot Deals API GET /crm/v3/objects/deals
   filter: dealstage='active', expiry_date <= today+90, renewal_status != 'outreach_active'
   fields: deal_id, deal_name, expiry_date, deal_value, owner_id,
           contact_id, last_activity_date, renewal_status

-> Score each deal:
   priority_score = (deal_value * 0.6) + (urgency_weight * 0.4)
   days_remaining = expiry_date - today
   urgency_weight: [0-30]=3, [31-60]=2, [61-90]=1

-> Dedupe check: skip if renewal_queue_date = today (idempotency guard)

-> HubSpot Deals API PATCH /crm/v3/objects/deals/{deal_id}
   body: { renewal_priority_score, renewal_queue_date }

-> Google Sheets API POST (append row to 'Renewal Queue' tab)
   row: [deal_id, deal_name, expiry_date, deal_value, owner_email,
         priority_score, queue_timestamp]

// HANDOFF -> Renewal Outreach Agent
ranked_deal_list: [{ deal_id, deal_name, expiry_date, deal_value,
                      contact_email, contact_first_name, owner_email,
                      priority_score, sequence_stage }]

// ============================================================
// AGENT 2: Renewal Outreach Agent
// ============================================================
FOR EACH deal IN ranked_deal_list:

  -> Select template by sequence_stage:
     '90_day' -> template_90_day_outreach
     '60_day' -> template_60_day_followup
     '30_day' -> template_30_day_final

  -> Merge fields: {contact_first_name}, {deal_name},
                   {expiry_date}, {account_manager_name}

  -> Gmail API POST /gmail/v1/users/{owner_email}/messages/send
     from: owner_email (per-account-manager OAuth token)
     to: contact_email
     subject: templated subject line
     body: templated HTML body
     response: message_id, thread_id

  -> Store in tracking table: { deal_id, thread_id, sequence_stage, sent_at }

  -> HubSpot Engagements API POST /crm/v3/objects/emails
     body: { dealId: deal_id, from: owner_email, to: contact_email,
             timestamp, subject, sequence_stage, gmail_thread_id }

  -> HubSpot PATCH deal: { renewal_status: 'outreach_active',
                           last_outreach_date: today }

// Reply detection loop (runs every 15 min via schedule)
  -> Gmail API GET /gmail/v1/users/{owner_email}/threads/{thread_id}
     IF thread.messages.length > 1 (reply detected):
       Apply Gmail label: 'renewal_replied'
       HubSpot PATCH deal: { renewal_status: 'client_replied' }
       Pause outreach sequence for deal_id

// HANDOFF -> Approval and Closing Agent
// (triggered by HubSpot deal property: approval_requested = true)
payload: { deal_id, deal_name, deal_value, contract_start_date,
           contract_end_date, contact_email, contact_name,
           account_manager_name, owner_email, is_standard_terms }

// ============================================================
// AGENT 3: Approval and Closing Agent
// ============================================================
IF is_standard_terms = false:
  -> HubSpot PATCH deal: { renewal_status: 'manual_proposal_required' }
  -> Slack API POST /chat.postMessage (to owner_email DM)
     text: 'Custom proposal required for {deal_name} — please prepare manually'
  STOP

IF is_standard_terms = true:
  -> Slack API POST /chat.postMessage (to #sales-approvals channel)
     Block Kit message: { deal_name, deal_value, contract_end_date,
                          account_manager_name, buttons: [Approve, Decline] }

  -> Await Slack interactive payload callback
     action_id: 'approval_approve' | 'approval_decline'
     timeout: 48h -> re-notify; 72h -> escalate via email to owner_email

  IF action_id = 'approval_approve':
    -> DocuSign Envelopes API POST /v2.1/accounts/{account_id}/envelopes
       templateId: renewal_standard_template_id
       templateRoles: [{ roleName: 'Client', email: contact_email,
                         name: contact_name },
                        { roleName: 'AccountManager', email: owner_email,
                          name: account_manager_name }]
       prefill: { deal_value, contract_start_date, contract_end_date }
       response: envelope_id
    -> Store in tracking table: { deal_id, envelope_id, sent_at }
    -> HubSpot PATCH deal: { renewal_status: 'contract_sent',
                             docusign_envelope_id: envelope_id }

  IF action_id = 'approval_decline':
    -> HubSpot PATCH deal: { renewal_status: 'approval_declined' }
    -> Slack DM to owner_email: 'Approval declined for {deal_name}'
    STOP

// DocuSign signed webhook callback
// POST {platform_webhook_url}/docusign/signed
// Verified via HMAC header X-DocuSign-Signature-1
  envelope_id -> lookup deal_id in tracking table

  -> HubSpot PATCH deal: { dealstage: 'closed_won_renewed',
                            contract_start_date, contract_end_date,
                            renewal_status: 'signed' }

  -> Xero Accounting API POST /api.xro/2.0/Invoices
     body: { Type: 'ACCREC', Status: 'DRAFT',
             Contact: { Name: contact_name },
             LineItems: [{ Description: 'Contract Renewal - {deal_name}',
                           Quantity: 1, UnitAmount: deal_value }],
             DueDate: contract_start_date }
     response: invoice_id

  -> HubSpot PATCH deal: { xero_invoice_id: invoice_id }

  -> Slack API POST /chat.postMessage (DM to owner_email)
     text: 'Contract signed and invoice raised for {deal_name}. Invoice ID: {invoice_id}'

// ============================================================
// END OF FLOW
// ============================================================
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool with one discrete workflow per agent (three workflows total). Each workflow is named to match the agent: 'CMA - Contract Monitoring Agent', 'ROA - Renewal Outreach Agent', 'ACA - Approval and Closing Agent'. All credentials are stored in a shared credential store accessible to all three workflows, with environment-scoped entries for sandbox and production. No credentials are hardcoded in workflow nodes.
Webhook configuration
Three inbound webhook endpoints required: (1) HubSpot deal property change webhook (expiry_date, renewal_status, approval_requested) routed to the Contract Monitoring Agent trigger; (2) Slack interactive payload endpoint for Block Kit button actions routed to the Approval and Closing Agent; (3) DocuSign Connect signed event webhook (envelope_completed) routed to the Approval and Closing Agent post-signature handler. All endpoints must be HTTPS with HMAC or token verification. DocuSign endpoint registered in DocuSign admin under Connect configurations.
Templating approach
Email body templates stored as reusable text assets within the orchestration layer (one per sequence stage: 90-day, 60-day, 30-day). Merge fields resolved at send time from the deal payload using standard string interpolation. DocuSign contract templates remain in DocuSign and are referenced by template_id only. No contract content is stored or generated outside DocuSign. Slack Block Kit messages constructed inline within the Approval and Closing Agent workflow using the payload fields.
Error logging
All agent errors (API failures, token expiry, missing field values, timeout events) are written to a Supabase table named 'renewal_automation_errors' with columns: error_id (UUID), agent_name, deal_id, error_type, error_message, timestamp, resolved (boolean). A Slack alert is fired to the build team monitoring channel (#fs-alerts) for any new row insertion. The Supabase table is also reviewed as part of the weekly UAT check during parallel run.
Testing approach
All three agents are built and tested against sandbox environments first: HubSpot sandbox account with cloned deal records, DocuSign developer sandbox with the standard renewal template published, and Xero demo company. Gmail send tests use a dedicated test account (not the live account manager accounts). Slack testing uses a dedicated test workspace. Production credentials are introduced only after the full end-to-end flow passes in sandbox. Parallel run (week 5) confirms production accuracy before the manual process is switched off.
Estimated total build time
Contract Monitoring Agent: 10 hours. Renewal Outreach Agent: 12 hours. Approval and Closing Agent: 14 hours. End-to-end integration testing, error handling build, and webhook verification: 2 hours. Total: 38 hours.
Pre-build blockers to resolve before any agent is coded: (1) HubSpot expiry_date field confirmed as a date-type property populated on all active deals. (2) DocuSign renewal template published and template_id recorded. (3) All account managers have authorised their Gmail OAuth connections. (4) Xero has a contact record for each active renewal client. (5) Slack app created with interactivity enabled and request URL pointing to the automation platform's public endpoint. Raise any of these with support@gofullspec.com if they are not ready at build kickoff.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Renewal & Contract Expiry Management.

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