Wavelink Operations Dashboard

Loading...
Operations
Documentation
System Health

Lambda Functions

Loading...

Step Functions

Loading...

EventBridge Rules

Loading...
Data Flow — Today

Tickets Processed

Loading...

AI Replies Applied

Loading...

Quotes Created

Loading...

End Customer Extraction

Loading...
Deal Sync — Today

HS → NS

Loading...

NS → HS

Loading...

Skipped

Loading...

Failed

Loading...
Active Issues
Data Flow
EventBridge (5 min) HubSpot Webhook (deal change) | | v v +----------------+ +---------------------+ | 1. Poll Tickets| | 8. Sync Deal/NS | | (today only) | | closedate, stage, | +-------+--------+ | team (bidir) | | +---------------------+ | [ticket IDs] v EventBridge (5 min) +----------------+ | | 2. Enrich | v | resolve co. | +---------------------+ +-------+--------+ | 7. Sync Presales | | | category to deal | | [enrichedData] +---------------------+ v +------------------+ EventBridge (1 min) | 3. Extract End | | | Customer (AI) | v +-------+----------+ +---------------------+ | | 6. Apply AI Replies | | [enrichedData] | create quote/deal | v | post to thread | +----------------+ +---------------------+ | 4. Call Webhook| ^ | Subrosa AI | | +-------+--------+ Subrosa sets ai_status | (FULL/PARTIAL/NONE/...) | [webhookResponse] v +----------------+ | 5. Update | | Ticket | +----------------+
Lambda Functions

1. Poll Tickets

hubspot-poll-tickets

Queries HubSpot for unprocessed tickets in the Wavelink Sales pipeline created today.

Step Functions Triggered every 5 min by EventBridge → Step Functions

Logic Flow

  1. Validate config (API key, pipelines)
  2. Search tickets: pipeline = Wavelink Sales, created today, ai_status not set
  3. Return list of ticket IDs (max 50 per batch)

Data Transformations

  • Pipeline config string → list of IDs
  • Ticket objects → array of ID strings

APIs

POST /crm/v3/objects/tickets/search

Metrics

None

2. Enrich Ticket

hubspot-enrich-ticket

Enriches ticket with contact and company data. Resolves "end user" companies to their reseller.

Step Functions Runs in parallel for each ticket

Logic Flow

  1. Fetch ticket details, contacts, and companies
  2. Check if primary company type = "end user" or "end customer"
  3. If yes: look up contact's other associated companies (v4 API)
  4. Find first non-end-user company (reseller)
  5. Update ticket association to reseller if found
  6. Return enriched data bundle

Data Transformations

  • Company type: case-insensitive match ("End Customer", "end user", etc.)
  • Contact → all companies (v4 API, not just primary)
  • End user company → resolved reseller company

APIs

GET /crm/v3/objects/tickets/{id}
GET /crm/v4/.../associations/contacts|companies
PUT /crm/v4/.../associations/... (if resolved)

Metrics

None

3. Extract End Customer

hubspot-extract-end-customer

Uses AWS Bedrock (Claude 3.5 Sonnet) to identify end customer mentioned in ticket subject/content, then searches HubSpot for that company.

Step Functions After enrich

Logic Flow

  1. Skip if: no company, company is end customer, or no ticket content
  2. Send subject + content to Bedrock AI with extraction prompt
  3. AI returns company name or "NONE"
  4. Search HubSpot companies by name (CONTAINS_TOKEN)
  5. If found: associate company to ticket, add conversation note
  6. If not found: add "could not be identified" note

Data Transformations

  • Ticket subject + content → Bedrock prompt
  • Bedrock response → extracted company name
  • "NONE" or empty → null

APIs

bedrock:InvokeModel (Claude 3.5 Sonnet, temp=0)
POST /crm/v3/objects/companies/search
PUT /crm/v4/.../associations/...

Metrics

EndCustomerExtracted, EndCustomerFound, EndCustomerNotFound, EndCustomerSkipped

4. Call Webhook

hubspot-call-webhook

Sends sanitized ticket data to Subrosa AI webhook for processing. Handles skip conditions (no company, Fortinet).

Step Functions After extract end customer

Logic Flow

  1. Extract email, company_id from enriched data
  2. Sanitize text: unescape HTML, remove control chars, normalize whitespace
  3. Skip if no company_id or company_id = "131" (Fortinet)
  4. Look up end customer company for ticket
  5. POST payload to Subrosa AI webhook (30s timeout)

Webhook Payload

FieldSource
EmailsPrimary contact email
Ticket subjectSanitized subject
Ticket contentSanitized body
Ticket IDNumeric ticket ID
Company IDReseller company_id property
End Customer IDEnd customer company_id (or "")
End Customer NameEnd customer name (or "")

Metrics

WebhookSuccess, WebhookSkipped, WebhookFailed

5. Update Ticket

hubspot-update-ticket

Updates ticket with processing status and adds a conversation note documenting the action taken.

Step Functions Final step in pipeline

Logic Flow

  1. If webhook skipped (no company): set ai_status = skip message
  2. If webhook skipped (Fortinet): set ai_status = skip message
  3. If webhook called: don't set ai_status (Subrosa AI sets it)
  4. Add single-line conversation note (e.g., "Sent to AI — email, Company 123")

Data Transformations

  • Skip reason + email + company → one-line message
  • Falls back to Engagements API if ticket has no conversation thread

APIs

PATCH /crm/v3/objects/tickets/{id}
POST /conversations/v3/.../messages (or Engagements v1 fallback)

Metrics

None

6. Apply AI Replies

hubspot-apply-ai-replies

Scheduled Lambda that creates HubSpot quotes/deals from AI responses and posts formatted content to ticket conversations.

EventBridge Every 1 minute

Logic Flow

  1. Search for tickets with processable ai_status (FULL, PARTIAL, NONE, etc.)
  2. If ai_items_json present: create Deal (AI Generated pipeline) + Quote + Line Items
  3. Associate deal to ticket (type 27), quote to deal (type 63)
  4. Format ai_reply_draft as HTML paragraphs
  5. Format ai_items_json as styled HTML table with pricing
  6. Post combined content to conversation thread
  7. Update ai_status: e.g., "FULL - Applied to conversation + quote"

Line Items Table Columns

ColumnSource FieldCalculation
Std Buy Pricebase_price_audDirect
Discount Codeprice_levelDirect (e.g., "4 Expert")
Discountbase_price_aud - list_price
Your Pricelist_priceFallback to rate
Line Totalqty × list_price

Quote Creation

Pipeline: AI Generated (1276261847), Stage: Quote Created, Expiry: +30 days, Sender: Sales @ Wavelink, Template: Basic (443041226216)

Metrics

AIRepliesApplied, QuotesCreated, QuoteValue

7. Sync Presales Activity

hubspot-sync-presales-activity

Syncs ticket category from presales pipeline tickets to associated deals' presales_activity field.

EventBridge Every 5 minutes

Logic Flow

  1. Search for tickets in presales pipeline with category set
  2. For each ticket: get associated deals
  3. Compare deal's current presales_activity with ticket category
  4. If different: update deal property
  5. If same: skip (idempotent)

Data Transformations

  • Ticket hs_ticket_category → Deal presales_activity (direct copy)

APIs

POST /crm/v3/objects/tickets/search
GET /crm/v3/objects/tickets/{id}/associations/deals
PATCH /crm/v3/objects/deals/{id}

Metrics

None

8. Sync Deal Closedate / Team

hubspot-sync-deal-closedate

Bidirectional sync between HubSpot deals and NetSuite opportunities. Handles closedate, stage, and team members.

Webhook HubSpot deal change   EventBridge Every 5 min (team poll)

Sync Directions

PropertyDirectionTrigger
Close DateHS → NSWebhook
Deal StageHS → NSWebhook
Team MembersHS ↔ NSWebhook + Poll
BDM / CMNS → HSBatch sync

Team Sync Logic (HS → NS)

  1. Get deal owner + collaborators from HubSpot
  2. Resolve each to NS employee: explicit map → exact name → LIKE → split name
  3. Preserve existing salesRoles from NS team
  4. Idempotency: skip if all HS members already on NS team (subset check)
  5. Debounce: skip if make__last_sync_datetime < 30s ago

Team Sync Logic (NS → HS)

  1. Get NS salesTeam + BDM + CM fields
  2. Map each to HubSpot owner by name
  3. Primary → hubspot_owner_id, others → collaborators

Metrics

DealsSyncedToNS, DealsSyncedFromNS, DealSyncSkipped, DealSyncFailed, DealCloseDateSynced, DealStageSynced

Reference Tables

AI Status Codes

CodeMeaningHas Quote Data
FULLComplete quote generatedYes
PARTIALPartial quote generatedYes
PARTIAL_MATCH_ITEMS_NOT_FOUNDSome items could not be matchedYes (partial)
NONENo quote — informational responseNo
NONE_IS_NOT_AN_ORDERNot an order requestNo
NONE_IS_ORDER_FOLLOWUPFollow-up to existing orderNo
NONE_FSS_ERROR_GET_RESELLERFSS error looking up resellerNo
NONE_FSS_ERROR_CREATE_OPPORTUNITYFSS error creating opportunityNo
NON_SIMPLE_ORDER_TOO_MANY_ITEMSToo many line items for simple orderNo
PreviewPreview / test modeVaries

After processing by Apply AI Replies, status gets suffix: e.g., "FULL - Applied to conversation + quote"

HubSpot ↔ NetSuite Deal Stage Mapping

HubSpot StageStage IDNS StatusNS ID
Discovery1539943921Discovery8
Present Discovery Findings1565386186Present Discovery Findings9
Pre Sales / Demo1565386187Pre Sales/Presentation/Demo10
Eval / Pilot / POC1565386188Eval/Pilot/POC11
Opportunity Qualified1539943922Opportunity Qualified22
Prepare Proposal1539943923Prepare and Present Proposal17
Negotiation1539943924Negotiation18
Sale Agreed1539943925Sale Agreed/Verbal Confirm19
Closed Won1539943926Closed Won13
Closed Lost1539943927Closed - Lost16
Closed Other1611558351Closed - Other24

HubSpot ↔ NetSuite Field Mapping (Deal Sync)

HubSpot Deal FieldNetSuite Opportunity FieldDirection
netsuite_opportunityInternal IDLink key
closedateexpectedCloseDateHS → NS
dealstageentityStatusHS → NS (mapped)
amountprojectedTotalRead only
hubspot_owner_idsalesTeam (primary)HS ↔ NS
hs_all_collaborator_owner_idssalesTeam (non-primary)HS ↔ NS
(set by NS sync)custbody_wl_transaction_bdmNS → HS
(set by NS sync)custbody_wl_transaction_cmNS → HS

Company Type Values

ValueMeaningAction
RESELLER / ResellerPartner / resellerUsed as-is for webhook
End Customer / end userEnd customerResolved to reseller via contact associations