Back to Pricing Approval Workflow

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

Pricing Approval Workflow

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

This document is the primary technical reference for the FullSpec team building the Pricing Approval Workflow automation. It covers the current-state process map with identified bottlenecks, full specifications for each of the three agents, the end-to-end data flow across all integrated tools, and the recommended build stack. All build decisions, field names, and configuration values in this document supersede earlier notes. The process owner and stakeholders have agreed the scope described here; any changes to discount thresholds, HubSpot property names, or approval logic must be reflected in this document before build proceeds.

01Process snapshot

Step
Name
Description
1
Rep Identifies Need for Non-Standard Price
Sales rep reviews the deal in HubSpot and decides the standard rate will not close the prospect. Notes the discount percentage or custom price needed. Time cost: 8 min per cycle.
2
Draft Approval Request Email or Message
Rep composes an email or Slack message to the sales manager with deal context, requested price, and reason for the exception. No standard template exists so detail varies widely. Time cost: 12 min per cycle.
3
Send Request to Sales Manager
Rep sends the message and waits. No acknowledgement mechanism exists so the rep has no visibility on whether it has been seen. Time cost: 3 min per cycle.
4
Manager Reviews Request
Sales manager opens the message, checks the deal record in HubSpot, and decides whether the margin is acceptable. Frequently happens hours or days later after being buried by other messages. BOTTLENECK. Time cost: 20 min per cycle.
5
Manager Seeks Finance Sign-Off If Required
For discounts above the threshold the manager loops in finance via a separate email chain, adding another untracked waiting period. BOTTLENECK. Time cost: 15 min per cycle.
6
Decision Communicated Back to Rep
Manager replies to the original message with an approval or denial. Context and conditions are sometimes lost if the thread has grown long. Time cost: 5 min per cycle.
7
Rep Logs Outcome in HubSpot
Rep manually updates the deal record with approved price, approver, and date. Frequently skipped under time pressure. Time cost: 8 min per cycle.
8
Approval Logged in Tracker Spreadsheet
A team admin records the request outcome in a shared Google Sheet used loosely as an audit log. Entries are inconsistent and sometimes made days after the fact. Time cost: 7 min per cycle.
9
Quote Document Updated and Sent to Prospect
Rep updates the quote or proposal document with the approved pricing and sends it to the prospect via DocuSign. Time cost: 18 min per cycle.
Time cost summary: Total manual time per cycle is 96 minutes across all parties. At 35 cycles per month this equates to approximately 56 hours per month and 5 hours per week of combined staff time. The automation replaces steps 1 through 8 in full: the Request Intake Agent replaces steps 1, 2, 3, and 8; the Approval Routing Agent replaces steps 4, 5, and 6; the CRM Update Agent replaces step 7. Step 9 (DocuSign quote delivery) remains a manual step performed by the rep and is out of scope for this build.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Request Intake Agent

Monitors HubSpot for deal records where a pricing exception has been raised, extracts all required fields, validates that no mandatory values are missing, and writes a structured row to the Google Sheets approval log. This agent is the entry point to the entire workflow. It must deduplicate incoming triggers to prevent the same deal generating multiple log entries, and it must surface a clear validation error to the submitting rep if required fields are absent rather than silently failing. Build complexity is Moderate given the field validation logic and the HubSpot webhook configuration required.

Trigger
A HubSpot deal property named 'pricing_exception_requested' is set to true, or a custom deal stage named 'Pricing Exception' is entered. The webhook fires on property change.
Tools
HubSpot (webhook source, deal record read), Google Sheets (approval log write)
Replaces steps
Steps 1, 2, 3, 8 (rep identification, manual request drafting, message send, and admin log entry)
Estimated build
8 hours, Moderate complexity
// Input (from HubSpot webhook payload)
deal_id: string               // HubSpot internal deal ID
deal_name: string             // Deal display name
rep_email: string             // Owner email from deal record
deal_value_usd: number        // Deal amount field
requested_discount_pct: number // Custom property: pricing_exception_discount_pct
exception_reason: string      // Custom property: pricing_exception_reason
standard_price_usd: number    // Custom property: standard_list_price
requested_price_usd: number   // Derived: standard_price_usd * (1 - requested_discount_pct/100)
gross_margin_impact: string   // Custom property: pricing_exception_margin_note
submitted_at: ISO8601 string  // Timestamp of webhook fire

// Validation checks before log write
REQUIRED: deal_id, deal_name, rep_email, deal_value_usd, requested_discount_pct, exception_reason
DEDUPE: query Google Sheets log for existing row where deal_id matches and status != 'Closed'
If duplicate found: suppress new row, send Slack DM to rep_email confirming existing request is open
If validation fails: send Slack DM to rep_email listing missing fields, halt workflow

// Output (written to Google Sheets: tab 'Approval Log', next empty row)
log_row_id: string            // UUID generated at write time
deal_id: string
deal_name: string
rep_email: string
deal_value_usd: number
requested_discount_pct: number
requested_price_usd: number
exception_reason: string
gross_margin_impact: string
submitted_at: ISO8601 string
status: 'Pending'             // Initial status value
approver_email: null          // Populated by Routing Agent
decision: null                // Populated by Routing Agent
decision_timestamp: null      // Populated by Routing Agent
approver_comment: null        // Populated by Routing Agent
  • HubSpot custom properties must be created before build starts: 'pricing_exception_requested' (boolean), 'pricing_exception_discount_pct' (number), 'pricing_exception_reason' (single-line text), 'standard_list_price' (number), 'pricing_exception_margin_note' (single-line text), 'approved_price_usd' (number), 'pricing_exception_status' (dropdown: Pending, Approved, Denied), 'approver_name' (single-line text), 'approval_date' (date). Confirm exact property internal names with the HubSpot admin before build.
  • HubSpot plan must be at least Starter tier to enable webhook subscriptions on property changes. Confirm this with [YourCompany.com] before build commences.
  • Google Sheets tab must be named exactly 'Approval Log' with column headers matching the output field names above in row 1. A second tab named 'Config' will store discount tier thresholds as named ranges for the Routing Agent to read.
  • Deduplication logic checks for an open row (status != 'Closed') with a matching deal_id before writing. A deal that was previously closed and reopened will generate a new log row; this is intentional.
  • If a required field is missing, the agent sends a Slack DM to the rep's email (matched against Slack workspace members) and halts. It does not write a partial row to the log.
  • Confirm with the sales manager and finance lead whether 'Closed' status covers both Approved and Denied outcomes, or whether Denied rows should remain queryable separately. This affects the dedupe condition.
Approval Routing Agent

Reads new Pending rows from the Google Sheets approval log, determines the correct approval path by comparing the requested discount percentage against configured tier thresholds, and dispatches a structured one-click approval request via Slack for standard approvals or Gmail for finance escalations. The agent polls the log on a defined schedule and monitors for responses, firing reminder messages if no decision is received within the configured window. When a decision arrives via Slack interactive callback or Gmail link click, the agent writes the outcome back to the log row and updates the deal status field in HubSpot to signal the CRM Update Agent. This is the most complex agent in the build due to the branching escalation logic, reminder cadence, and the Slack interactive component requirement.

Trigger
A new row with status 'Pending' appears in the Google Sheets 'Approval Log' tab. The agent polls this tab every 2 minutes via a scheduled workflow trigger.
Tools
Google Sheets (log read and write), Slack (interactive approval message and reminders), Gmail (finance escalation email with approval link)
Replaces steps
Steps 4, 5, 6 (manager review and decision, finance sign-off loop, decision communicated to rep)
Estimated build
12 hours, Complex complexity
// Input (read from Google Sheets 'Approval Log' row where status = 'Pending')
log_row_id: string
deal_id: string
deal_name: string
rep_email: string
deal_value_usd: number
requested_discount_pct: number
requested_price_usd: number
exception_reason: string
gross_margin_impact: string
submitted_at: ISO8601 string

// Tier logic (thresholds read from Google Sheets 'Config' tab, named ranges)
TIER_1_MAX_PCT: number        // e.g. 10 — manager-only approval
TIER_2_MAX_PCT: number        // e.g. 20 — manager approves, finance notified
TIER_3_THRESHOLD_PCT: number  // e.g. 21+ — finance approver required, manager cc'd
REMINDER_HOURS: number        // e.g. 4 — hours before first reminder fires
ESCALATION_HOURS: number      // e.g. 24 — hours before escalation to next approver

// Slack approval message fields (sent to manager Slack user ID, looked up by rep_email match)
slack_channel: DM to approver_slack_user_id
message_blocks: deal_name, deal_value_usd, requested_discount_pct, requested_price_usd,
                exception_reason, gross_margin_impact, approve_button, deny_button
callback_id: 'pricing_approval_' + log_row_id

// Gmail escalation fields (sent when requested_discount_pct >= TIER_3_THRESHOLD_PCT)
to: finance_approver_email    // Read from 'Config' tab named range FINANCE_EMAIL
cc: manager_email             // Read from 'Config' tab named range MANAGER_EMAIL
subject: 'Finance Approval Required: ' + deal_name
body_template: structured HTML with deal_name, requested_discount_pct, deal_value_usd,
               requested_price_usd, exception_reason, approve_url, deny_url
approve_url: webhook endpoint + '?action=approve&row=' + log_row_id + '&token=' + signed_token
deny_url:    webhook endpoint + '?action=deny&row=' + log_row_id + '&token=' + signed_token

// On approval or denial (Slack callback or Gmail link click)
decision: 'Approved' | 'Denied'
approver_email: string        // Extracted from Slack payload or URL token
approver_name: string         // Looked up from Slack profile or Config tab
approver_comment: string      // Optional; from Slack modal or URL param
decision_timestamp: ISO8601 string

// Output (written back to Google Sheets 'Approval Log' row, matched by log_row_id)
status: 'Approved' | 'Denied'
approver_email: string
approver_name: string
approver_comment: string
decision_timestamp: ISO8601 string

// Output (written to HubSpot deal property to signal CRM Update Agent)
pricing_exception_status: 'Approved' | 'Denied'
  • A Slack app must be created in the [YourCompany.com] Slack workspace with the following scopes: chat:write, im:write, users:read, users:read.email, and interactive components enabled. A workspace admin must approve the app installation before build starts.
  • The Slack interactive component callback URL must point to a stable webhook endpoint hosted within the automation platform. Confirm the platform can expose a public HTTPS endpoint for Slack callbacks before build.
  • Approval link tokens for Gmail escalations must be signed using HMAC-SHA256 with a secret stored in the credential store. Tokens must expire after 72 hours. If the link is clicked after expiry the agent must return a clear error page and log the attempt.
  • Tier thresholds (TIER_1_MAX_PCT, TIER_2_MAX_PCT, TIER_3_THRESHOLD_PCT), reminder timing (REMINDER_HOURS, ESCALATION_HOURS), finance approver email, and manager email must all be stored as named ranges in the 'Config' tab, not hardcoded in the workflow. This allows threshold changes without touching the automation.
  • The reminder logic must track whether a reminder has already been sent for a given log_row_id to prevent repeated reminders. Store a 'reminder_sent_at' field in the log row.
  • If the Slack user ID lookup by rep_email fails (the rep is not in the Slack workspace), fall back to sending the approval request via Gmail to manager_email. Log the fallback event to the error table.
  • Confirm with the sales manager whether a 'deny with comment' flow is required in Slack (modal popup) or whether free-text comment is optional. This affects the Slack block kit design and callback handling.
CRM Update Agent

Monitors the Google Sheets approval log for rows where a final decision has been recorded and the corresponding HubSpot deal property 'pricing_exception_status' has been updated to Approved or Denied. On detecting a completed decision, the agent writes the approved price, decision date, approver name, and approval status back to the HubSpot deal record properties, then sends a Slack direct message to the sales rep confirming the outcome with all relevant details. This agent is the final automated step in the workflow; it has no branching logic and is the simplest of the three agents.

Trigger
The HubSpot deal property 'pricing_exception_status' is set to 'Approved' or 'Denied' by the Routing Agent. The agent subscribes to the same HubSpot webhook as the Intake Agent, filtered to this property change.
Tools
HubSpot (deal record property write), Slack (rep DM notification)
Replaces steps
Step 7 (rep logs outcome in HubSpot CRM)
Estimated build
5 hours, Simple complexity
// Input (from HubSpot webhook: pricing_exception_status property change)
deal_id: string
pricing_exception_status: 'Approved' | 'Denied'

// Lookup (read from Google Sheets 'Approval Log', matched by deal_id, status != Pending)
log_row_id: string
deal_name: string
rep_email: string
requested_price_usd: number
approved_price_usd: number    // = requested_price_usd if Approved, else null
approver_name: string
approver_comment: string
decision_timestamp: ISO8601 string
decision: 'Approved' | 'Denied'

// Output (written to HubSpot deal record properties via PATCH /crm/v3/objects/deals/{deal_id})
approved_price_usd: number    // Custom property, null if Denied
pricing_exception_status: 'Approved' | 'Denied'
approver_name: string         // Custom property
approval_date: date string    // Derived from decision_timestamp, format YYYY-MM-DD

// Output (Slack DM to rep, looked up by rep_email against Slack workspace members)
recipient: rep_slack_user_id
message: deal_name + decision + approved_price_usd (if Approved) + approver_name + approver_comment
fallback: if Slack user lookup fails, send Gmail to rep_email with same content
  • This agent must not fire until the Routing Agent has written a complete decision row to the log. The HubSpot property change trigger is the reliable signal; do not rely on polling the Sheets log alone.
  • If the decision is Denied, set approved_price_usd to null (do not write zero) in HubSpot to avoid misleading reporting.
  • Slack DM to the rep uses the same user lookup pattern as the Routing Agent. If the lookup fails, fall back to Gmail and log the event.
  • The agent must be idempotent: if the HubSpot webhook fires twice for the same deal_id and status, the second execution must detect the deal record already has a decision written and exit without duplicating the Slack DM.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow, Pricing Approval Workflow
// ============================================================
// TRIGGER: Rep sets pricing_exception_requested = true on HubSpot deal
// ============================================================
HubSpot webhook fires on property change: pricing_exception_requested -> true
  payload fields: deal_id, deal_name, rep_email, deal_value_usd,
                  pricing_exception_discount_pct, pricing_exception_reason,
                  standard_list_price, pricing_exception_margin_note, submitted_at

// ============================================================
// AGENT 1: Request Intake Agent
// ============================================================
Step 1.1 — Receive webhook payload from HubSpot
  extract: deal_id, deal_name, rep_email, deal_value_usd,
           requested_discount_pct, exception_reason, standard_list_price,
           gross_margin_impact, submitted_at
  derive:  requested_price_usd = standard_list_price * (1 - requested_discount_pct / 100)

Step 1.2 — Validate required fields
  REQUIRED: deal_id, deal_name, rep_email, deal_value_usd,
            requested_discount_pct, exception_reason
  IF any field missing:
    -> Slack DM to rep_email: 'Your pricing request is missing [field list]. Please update the deal record.'
    -> HALT workflow for this deal_id

Step 1.3 — Deduplicate against Google Sheets 'Approval Log'
  query: SELECT log_row_id WHERE deal_id = :deal_id AND status != 'Closed'
  IF duplicate row found:
    -> Slack DM to rep_email: 'A pricing request for [deal_name] is already open (ID: [log_row_id]).'
    -> HALT workflow for this deal_id

Step 1.4 — Write structured row to Google Sheets 'Approval Log'
  write fields: log_row_id (UUID), deal_id, deal_name, rep_email, deal_value_usd,
                requested_discount_pct, requested_price_usd, exception_reason,
                gross_margin_impact, submitted_at, status='Pending',
                approver_email=null, decision=null, decision_timestamp=null,
                approver_comment=null, reminder_sent_at=null

// --- AGENT HANDOFF: Intake Agent -> Approval Routing Agent ---
// Signal: new row with status='Pending' visible in 'Approval Log' tab
// Routing Agent polls tab every 2 minutes; picks up log_row_id

// ============================================================
// AGENT 2: Approval Routing Agent
// ============================================================
Step 2.1 — Read new Pending rows from Google Sheets 'Approval Log'
  read: all rows where status = 'Pending' and approver_email IS NULL

Step 2.2 — Read tier thresholds from Google Sheets 'Config' tab
  TIER_1_MAX_PCT      (e.g. 10)
  TIER_2_MAX_PCT      (e.g. 20)
  TIER_3_THRESHOLD_PCT (e.g. 21)
  REMINDER_HOURS      (e.g. 4)
  ESCALATION_HOURS    (e.g. 24)
  MANAGER_EMAIL       (e.g. manager@yourcompany.com)
  FINANCE_EMAIL       (e.g. finance@yourcompany.com)

Step 2.3 — Determine approval path
  IF requested_discount_pct <= TIER_1_MAX_PCT:
    route = 'SLACK_MANAGER_ONLY'
    approver_email = MANAGER_EMAIL
  ELSE IF requested_discount_pct <= TIER_2_MAX_PCT:
    route = 'SLACK_MANAGER_NOTIFY_FINANCE'
    approver_email = MANAGER_EMAIL
    notify_email = FINANCE_EMAIL
  ELSE:
    route = 'GMAIL_FINANCE_ESCALATION'
    approver_email = FINANCE_EMAIL
    cc_email = MANAGER_EMAIL

Step 2.4a — IF route = SLACK_MANAGER_ONLY or SLACK_MANAGER_NOTIFY_FINANCE:
  lookup approver Slack user ID by approver_email
  IF lookup fails: fall back to Gmail to approver_email, log fallback
  send Slack DM with Block Kit message:
    fields: deal_name, deal_value_usd, requested_discount_pct, requested_price_usd,
            exception_reason, gross_margin_impact
    actions: [Approve] button, [Deny] button
    callback_id: 'pricing_approval_' + log_row_id
  update 'Approval Log' row: approver_email = approver_email, reminder_sent_at = null

Step 2.4b — IF route = GMAIL_FINANCE_ESCALATION:
  generate signed token: HMAC-SHA256(log_row_id + action, SECRET_KEY), expires 72 hrs
  approve_url = webhook_base + '?action=approve&row=' + log_row_id + '&token=' + signed_token
  deny_url    = webhook_base + '?action=deny&row=' + log_row_id + '&token=' + signed_token
  send Gmail:
    to: FINANCE_EMAIL
    cc: MANAGER_EMAIL
    subject: 'Finance Approval Required: ' + deal_name
    body: structured HTML with deal_name, deal_value_usd, requested_discount_pct,
          requested_price_usd, exception_reason, gross_margin_impact,
          approve_url, deny_url

Step 2.5 — Reminder and escalation monitor (runs on schedule every 30 min)
  query 'Approval Log': rows where status='Pending' and decision IS NULL
  FOR each row:
    IF now - submitted_at >= REMINDER_HOURS AND reminder_sent_at IS NULL:
      resend Slack DM (or Gmail) reminder with same callback_id / links
      write reminder_sent_at = now to log row
    IF now - submitted_at >= ESCALATION_HOURS AND decision IS NULL:
      send Slack DM to MANAGER_EMAIL: '[deal_name] pricing request has not been actioned for 24 hours.'
      log escalation event to error table

Step 2.6 — Receive decision (Slack callback or Gmail link click)
  Slack: action_type (approve|deny) from callback payload, approver_slack_user_id, optional comment
  Gmail: action (approve|deny) from URL param, validate signed token (reject if expired)
  derive: approver_name (Slack profile lookup or Config tab), decision_timestamp = now

Step 2.7 — Write decision to Google Sheets 'Approval Log' row (matched by log_row_id)
  status: 'Approved' | 'Denied'
  approver_email, approver_name, approver_comment, decision_timestamp

Step 2.8 — Write signal to HubSpot deal record
  PATCH /crm/v3/objects/deals/{deal_id}
  properties: { pricing_exception_status: 'Approved' | 'Denied' }

// --- AGENT HANDOFF: Approval Routing Agent -> CRM Update Agent ---
// Signal: HubSpot webhook fires on pricing_exception_status property change
// CRM Update Agent receives deal_id and new status value

// ============================================================
// AGENT 3: CRM Update Agent
// ============================================================
Step 3.1 — Receive HubSpot webhook: pricing_exception_status changed
  payload: deal_id, pricing_exception_status ('Approved' | 'Denied')

Step 3.2 — Idempotency check
  read HubSpot deal: check if approval_date is already populated
  IF yes: log 'duplicate trigger suppressed for deal_id' and EXIT

Step 3.3 — Read decision details from Google Sheets 'Approval Log'
  query: SELECT * WHERE deal_id = :deal_id AND status IN ('Approved','Denied')
  read: deal_name, rep_email, requested_price_usd, approver_name,
        approver_comment, decision_timestamp, decision
  derive: approved_price_usd = requested_price_usd IF decision='Approved' ELSE null
          approval_date = DATE(decision_timestamp) // format YYYY-MM-DD

Step 3.4 — Write final outcome to HubSpot deal record
  PATCH /crm/v3/objects/deals/{deal_id}
  properties:
    approved_price_usd: number | null
    pricing_exception_status: 'Approved' | 'Denied'
    approver_name: string
    approval_date: YYYY-MM-DD

Step 3.5 — Send Slack DM to rep
  lookup rep Slack user ID by rep_email
  IF lookup fails: send Gmail to rep_email with same content, log fallback
  message content: deal_name, decision, approved_price_usd (if Approved),
                   approver_name, approver_comment

// ============================================================
// END OF AUTOMATED WORKFLOW
// Remaining manual step: rep updates quote in DocuSign (Step 9, out of scope)
// ============================================================
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform configured with one discrete workflow per agent (three workflows total: Request Intake, Approval Routing, CRM Update). Credentials for HubSpot, Slack, Gmail, and Google Sheets are stored in a shared credential store accessible by all three workflows. No credentials are stored as inline environment variables within workflow nodes. The Approval Routing Agent requires a fourth auxiliary workflow for the reminder and escalation monitor, running on a 30-minute cron schedule.
Webhook configuration
Two inbound webhook subscriptions on HubSpot: one on the property 'pricing_exception_requested' (triggers Intake Agent), one on 'pricing_exception_status' (triggers CRM Update Agent). Both subscriptions require a HubSpot private app with the following scopes: crm.objects.deals.read, crm.objects.deals.write, crm.schemas.deals.read. One inbound webhook endpoint within the automation platform receives Slack interactive component callbacks (callback_id pattern: 'pricing_approval_*'). One inbound webhook endpoint receives Gmail approval link clicks for finance escalations; this endpoint validates the HMAC-SHA256 token before processing any action.
Templating approach
Slack messages are built using Block Kit JSON with a Section block for deal details, a Divider, and an Actions block containing Approve and Deny buttons. The deny path should trigger a modal with an optional comment input field. Gmail escalation emails use a stored HTML template with mustache-style placeholders ({{deal_name}}, {{requested_discount_pct}}, {{approve_url}}, {{deny_url}}, etc.) rendered at send time. Templates are stored as text nodes within the workflow, not as external files, to keep the build self-contained.
Error logging
All agent exceptions (validation failures, Slack user lookup failures, expired token attempts, duplicate trigger suppressions, and escalation events) are written to a dedicated Supabase table named 'pricing_workflow_errors' with columns: error_id (UUID), agent_name, deal_id, log_row_id, error_type, error_detail, occurred_at (ISO8601). A Slack alert is sent to a designated ops channel (e.g. #automation-alerts) for any error of type 'validation_failure' or 'escalation_no_response'. Routine fallback events (Slack user not found, Gmail fallback used) are logged to the table but do not trigger an alert.
Testing approach
All three agents are built and tested in a sandbox environment first: HubSpot sandbox account, a test Google Sheets workbook, a test Slack workspace, and a Gmail test alias. Each agent is tested in isolation before end-to-end sequences are run. The Slack interactive component is tested using Slack's Block Kit Builder before being wired to a live webhook. Finance escalation Gmail links are tested with a short token expiry (5 minutes) in sandbox to confirm expiry handling. The full end-to-end test sequence covers at least three discount tiers, one denied request, one reminder trigger, and one duplicate submission. Sandbox test results are documented before any credentials are switched to production.
Estimated total build time
Request Intake Agent: 8 hours. Approval Routing Agent: 12 hours. CRM Update Agent: 5 hours. End-to-end integration testing and error logging setup: 3 hours. Total: 28 hours across a 4-week delivery schedule as per the agreed delivery plan.
Before build starts, the following must be confirmed in writing with [YourCompany.com]: (1) All nine HubSpot custom deal properties created with agreed internal names. (2) HubSpot plan confirmed at Starter tier or above for webhook access. (3) Slack app approved by workspace admin with interactive components enabled. (4) Discount tier thresholds and escalation timing agreed with the sales manager and finance lead and entered into the Google Sheets 'Config' tab. (5) Finance approver email and manager email confirmed for the Config tab. (6) Supabase project provisioned and 'pricing_workflow_errors' table created. Any of these items outstanding at build start will extend the timeline.
Support contact for build queries and post-launch issues: support@gofullspec.com
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Pricing Approval Workflow.

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