Manual Test Runbook — A5: HubSpot discovery trigger
Owner: Sagar | Time: ~45 min | Requires: HubSpot sandbox + GitHub token + Postmark/email service
Purpose
Verify that the A5 Custom Code Action dispatches the G4 discovery workflow and sends an offer email when a HubSpot Deal moves to "Qualified".
Prerequisites
- HubSpot sandbox account with a Deals pipeline.
- GitHub token (fine-grained PAT with
actions:writeonsnowops-automationrepo). - Postmark account (or another email provider configured).
- Sandbox Deal has these custom properties created:
discovery_tenant_id(single-line text)discovery_subscription_id(single-line text)discovery_subscription_display_name(single-line text, optional)discovery_status(single-line text, default null)discovery_triggered_at(datetime)contact_email(single-line text, mapped from primary contact)contact_firstname(single-line text, mapped from primary contact)company_name(single-line text)
Steps
1. Unit test suite
- All 4 test suites pass, including
discovery-trigger/action.test.ts(6 tests).
2. Create HubSpot Private App secret
In HubSpot sandbox:
- Settings → Integrations → Private Apps.
- Create/use app with scopes:
-
crm.objects.deals.read -
crm.objects.deals.write -
crm.objects.contacts.read - Copy token → store as
HUBSPOT_TOKEN_A5.
3. Set up GitHub fine-grained PAT
On github.com:
- Settings → Developer settings → Personal access tokens → Fine-grained tokens.
- Create token with:
- Repository:
snowops/snowops-automation - Permissions:
actions:write - Copy → store as
GITHUB_PAT_A5.
4. Set up Postmark (or test email service)
- Create a Postmark account (free tier is fine for testing).
- Verify the sender domain (test emails can go to any address).
- Copy server token → store as
POSTMARK_TOKEN_A5.
5. Create the Custom Code Action in HubSpot
Navigate to Automation → Custom Code:
- Create action
Discovery Trigger (A5). - Language: Node.js 20.
- Input fields:
dealId(type: number, required)- Output fields:
triggered(boolean)skip_reason(string, nullable)workflow_dispatched(boolean)email_sent(boolean)- Secrets in Private App settings:
HUBSPOT_PRIVATE_APP_TOKEN→ from step 2.GITHUB_DISPATCH_TOKEN→ from step 3.EMAIL_API_TOKEN→ Postmark server token from step 4.EMAIL_FROM→ sender email (e.g.,no-reply@snowops.io).GH_OWNER→snowopsGH_REPO→snowops-automationGH_WORKFLOW_FILE→discovery-run.ymlGH_WORKFLOW_REF→main(or a test branch)
Paste the compiled code from dist/discovery-trigger/action.js (entry point: main export).
6. Create a test Deal in HubSpot
- Create a Deal: "Test Co — Cloud Secure".
- Fill in custom properties:
discovery_tenant_id:11111111-2222-3333-4444-555555555555(valid GUID format)discovery_subscription_id:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee(valid GUID format)discovery_subscription_display_name:Test Co Sandboxcontact_email:alice@testco.com(your test email address)contact_firstname:Alicecompany_name:Test Co- Note the Deal ID.
7. Create a test workflow
In HubSpot Workflows:
- Trigger: Deal stage changed to
Qualified. - Action: Run Custom Code →
Discovery Trigger (A5). - Input mapping:
dealId← Deal ID (built-in).
8. Move the Deal to Qualified
- In HubSpot, open the Deal from step 6.
- Change deal stage to
Qualified. - Workflow enrolls the deal.
- Workflow runs the action.
- Wait 5–10 seconds for the action to execute.
9. Verify the GitHub workflow dispatch
- Open snowops-automation Actions.
- Look for a new run of
discovery-runworkflow. - Run inputs should show:
client_name:test-cotenant_id: (the GUID you filled in)subscription_id: (the GUID you filled in)subscription_display_name:Test Co Sandboxhubspot_deal_id: (the Deal ID from step 6)environment:discovery-default
10. Verify the offer email
- Check your test email inbox (or Postmark logs if testing internally).
- Email should:
- From: the sender email you configured.
- To:
alice@testco.com. - Subject: "SnowOps free Azure discovery audit for Test Co".
- Body mentions:
- "Hi Alice," greeting.
- Links to G0 bootstrap docs.
- Booking URL.
- Explanation of the three IDs needed (tenant, subscription, SP).
11. Verify HubSpot Deal was patched
- Refresh the Deal in HubSpot.
- Check that:
-
discovery_status=triggered. -
discovery_triggered_at= timestamp (ISO format).
12. Test idempotency
- Re-run the workflow on the same Deal (e.g., re-enroll or trigger manually if HubSpot allows).
- The action should return:
triggered: falseskip_reason: "already_triggered"- No GitHub dispatch happens.
- No email sent.
13. Test validation failures
Create additional test Deals with missing/invalid properties:
- Missing
discovery_tenant_id: - Verify
skip_reason:missing_or_invalid_tenant_id. - No dispatch, no email.
- Invalid GUID format in
discovery_subscription_id: - Same skip behavior.
- Missing
contact_email: skip_reason:missing_contact_email.
Pass criteria
- Unit tests all pass (6 discovery-trigger tests).
- Custom Code Action created and available in HubSpot workflows.
- Deal move to Qualified triggers the workflow.
- GitHub
discovery-runworkflow is dispatched with correct inputs. - Offer email is received at the contact's email address.
- Deal custom properties are updated (
discovery_status,discovery_triggered_at). - Idempotent: re-firing does not re-dispatch or re-email.
- Validation failures are handled gracefully (skip_reason returned).
Failure modes & escalation
| Symptom | Action |
|---|---|
| GitHub dispatch 404s | Check PAT token has actions:write scope on correct repo. Verify GH_OWNER, GH_REPO, GH_WORKFLOW_FILE secrets are correct. |
| Email fails to send | Check Postmark token is valid and from address is verified. Look at Postmark logs. |
| Deal not enrolled in workflow | Check workflow trigger is set to stage change → Qualified. Manually trigger workflow if HubSpot UI allows. |
| Custom Code Action not available | Ensure it's committed to the Private App. Reload HubSpot page. |
| Invalid email address format not rejected | Check regex in isPlausibleEmail() — it requires user@domain.ext format. |
Sign-off
- Tester: ___ | Date: _ | Result: PASS / FAIL / N/A
- Notes: