Wiki · Workflow companion

New customer onboard

Sales adds a new customer. The cascade handles duplicate check, NS customer record creation, default customer_programs assignment (Net 30, default MDF tier), AR setup, and a welcome email — plus building a first-quote-context bundle so the first quote is ready faster.

Stub · Contract present, runner stub-only
What this is

New customer onboard

New customer onboarding is the simpler sibling of new_assembly_item. The cascade is narrower but the duplicate check is critical — a duplicate customer creates two AR records, two pricing trees, and a downstream mess that takes hours to unwind.

The 10-step cascade culminates in a "first-quote-context bundle" — a pre-built reference dossier so when the customer's first quote drafts, the AI has the full context (terms, MDF tier, sales rep, expected SKU mix) already loaded.

Risk level 3 (medium). The block-severity duplicate check is the main guard.

When to use it

Trigger conditions

Heuristic

Duplicate check runs by name AND email — both lowercase-matched. Common false positives are franchise locations of the same parent (e.g. "Driscoll Foods" vs "Driscoll Foods - Bronx"); Mike confirms with the sales rep before overriding.

Step-by-step what happens

The 4 beats

  1. 01

    Write customer record

    INSERT into customers with company name, contact, billing/shipping addresses, terms, credit limit, and sales rep.

    Writes customers
    Time ~80ms
    Kind d1_write
    Status stub
  2. 02

    NS customer record

    Enqueue NS customer creation. The NS internal id back-fills into D1 when the push drains.

    Writes NetSuite customer record
    Time ~3–8s
    Kind ns_push
    Status stub
    Note retry · 3 attempts exponential
  3. 03

    Assign default customer_programs

    INSERT default customer_programs rows: payment_terms=Net 30, default MDF tier, default rebate tier. These can be edited later via program-specific workflows.

    Writes customer_programs
    Time ~120ms
    Kind d1_write
    Status stub
  4. 04

    Welcome email draft

    AI drafts a welcome email introducing GFS, listing the sales rep, and providing the first-quote-context. Mike approves before send.

    Writes outbound_email_log
    Time ~80ms
    Kind hitl_email_draft
    Status stub
    Note target · customer
Outcomes

What's different after the workflow runs

Duplicate check
Blocked
by name + email
NS customer
Created
≤ 15 min
Default programs
Assigned
Net 30, default MDF
Welcome email
Drafted
awaiting Mike's tap
Failure modes

What can go wrong and how to recover

Duplicate customer found

Precondition blocks. Mike confirms with sales: is this truly a new location? If yes, override with a tagged note. If no, redirect to customer_record_review for the existing record.

Invalid email format

Precondition warns. Often a typo; sales fixes and re-stages.

NS push fails

Retry policy is 3 exponential attempts. D1 holds the proposed state; drainer recovers within 15 minutes.

Customer onboard from bid award

When chained from bid_award_notification, the bid context auto-populates the first-quote-context bundle — saves Mike a few minutes.

Related

Adjacent workflows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractworkflow_definitions WHERE workflow_type='new_customer'
Duplicate checkname (lower) OR email (lower)
Default termsNet 30
NS RESTletcustomscript_gfs_platform_query
First-quote-contextpre-built dossier for first quote
Reflexion tagnew_customer
Risk level3
Expected duration~20 min
Triggermanual · sources=sales_intake_form
// Duplicate check is case-insensitive on both fields const dup = await db.prepare( "SELECT id, companyname FROM customers " + "WHERE LOWER(companyname) = LOWER(?) OR LOWER(email) = LOWER(?)" ).bind(name, email).first(); if (dup) throw new Error('duplicate_customer');
Changelog

Dated trail · spot stale claims

Dated trail of when this doc was last touched, what changed, and what to look at if it feels stale.

DateRoundChangeTouched by
2026-05-26R586Added CHANGELOG · SCHEMA · RUNBOOK · BACKLOG sections — wiki became best-in-class operating documentation.Mike + Claude
2026-05-25R584/R585Wiki originally shipped — 8-section structure (hero / what / when / steps / outcomes / failure-modes / related / for-developers).Mike + Claude
If today is more than 60 days past the latest changelog row, treat live system behavior as the source of truth. The doc may have drifted — verify against the workflow contract in workflow_definitions WHERE workflow_type='new_customer' before acting on these claims.
Schema · data contract

The machine-readable spec

Canonical fields, table names, endpoint signatures. What code should match, what tests should assert. workflow_type · new_customer · risk_level · 3.

Inputs (required + optional)

FieldTypeDescription
legal_namestringLegal entity name. Required.
contact_emailstringPrimary contact.
billing_addressjsonAddress object.
customer_typestring'school' | 'commercial' | 'distributor'
credit_termsstring'net30' | 'net60' | 'prepay'

D1 tables written

TableOperationTrigger
proposed_actionsINSERTCustomer creation proposal
customersINSERT (post-HITL)D1 mirror
ns_pending_pushesINSERTNS customer creation
customer_programsINSERTDefault programs based on type

Endpoints called

MethodPathPurpose
POST/api/workflow/executeRunner entrypoint
POST/api/proposed-actions/:id/decideHITL approval

Events fired

event_typeWhenSubscribers
customer.createdPost-HITL + NS pushcustomer_health init
Runbook · when it breaks

It broke at 2am — what now

Different from "how do I use this." This is the page Mike pulls up when something is wrong: logs to check, recovery steps, who to escalate to.

Scenario · Customer created in D1 but never pushed to NS

ns_push stub.

  1. Check: SELECT * FROM ns_pending_pushes WHERE entity_ref LIKE 'customer:%'
  2. Manual: Create in NS; reconcile customers.ns_internal_id.

Scenario · Duplicate customer created (same legal name)

No dedupe check.

  1. Find dupes: SELECT legal_name, COUNT(*) FROM customers GROUP BY legal_name HAVING COUNT(*) > 1
  2. Merge: Pick canonical; update FK references; soft-delete dupe.
  3. Prevent: Add dedupe check in new_customer workflow before INSERT.

Scenario · Customer type set wrong — pricing tier off

Type drives default tier.

  1. Update: UPDATE customers SET customer_type=?, pricing_tier=? WHERE id=?
  2. Push to NS: propose_customer_update

Logs to check

Kill switch · emergency stop

If this workflow is misbehaving in a high-impact way (creating bad proposed_actions in volume, pushing wrong things to NS), flip a kill switch:

See kill-switches-state-machine.html for the full state machine + recovery procedure.

Escalation

Primary: Mike Levine (single-admin) · mikelevine@globalfoodsolutions.co. For prolonged outage during business hours, notify warehouse lead + accounting lead so they can defer dependent work.

Backlog · open questions

What's not done · what's uncertain

What's not done, what's uncertain, what we punted. Captured so it survives context switches and doesn't die in someone's head.