Clyde Dispatch Group
Project Dashboard
⌂ Taz HQ
0%
Data Flow — End to End
Load Board
123LB (mock)
load-fetcher.js
polls every 10 min
Telegram
YES / NO buttons
cdg-ops.js
handleApproval()
GHL SMS
driver offer
Driver SMS Reply
YES within 10 min
retell-webhook.js
/webhook/ghl-inbound
Retell AI
Clyde agent
Broker Phone Call
outbound negotiation
Post-Call Webhook
retell-webhook.js
/webhook/retell
GHL Contact Update
load_status field
SQLite DB
loads, calls, brokers
VPS — OpenClaw
HostHostinger KVM 2
IP (public)187.77.222.188
IP (Tailscale)100.88.255.43
UserTaz
OSUbuntu, user systemd
VPS renewalAuto-renews 2026-04-24
GHL trialExpires 2026-04-19
Running Services
cdg-ops.service cdg-ops.js
cdg-webhook.service retell-webhook.js :3000
Caddy HTTPS clydedispatch.com
RETELL_SKIP_SIG_VERIFY true — disable pre-prod
Load fetcher Mock mode (no API key)
Webhook Routes
POST /webhook/intakeCarrier intake form
POST /webhook/retellRetell post-call
POST /webhook/ghl-inboundDriver SMS reply
Caddy proxy/webhook/* → :3000
AuthHMAC-SHA256 + rate limit
Database — SQLite (WAL)
Path~/.openclaw/workspace/cdg/cdg.db
loadsAll load records + state machine
callsRetell call logs
brokersBroker reputation table
lane_ratesNegotiated rates by lane (seed data)
External Integrations
Retell AIVoice agent + post-call webhook
GHLCRM, contacts, SMS, pipelines
Telegram@ClydeDispatch_bot — Taz approvals
PandaDocAuto-send agreement on intake
Google Workspacedispatch@clydedispatch.com (SMTP)
Email Deliverability
SPFLive — clydedispatch.com
DMARCLive — clydedispatch.com
DKIMTXT added 2026-04-14 — verify in Google Workspace Admin
Next stepmail-tester.com → 9+/10 → DAT / Truckstop emails
Domain & Web
Domainclydedispatch.com (Namecheap)
SSLLet's Encrypt via Caddy (auto)
Web root~/.openclaw/workspace/cdg-web/
Intake formclydedispatch.com/apply.html
Caddy config/etc/caddy/Caddyfile
CDG Software — Component Manual
VPS workspace: /home/Taz/.openclaw/workspace/cdg/

All processes run as user systemd services under the Taz user. Credentials are in .env (chmod 600) and Bitwarden — no hardcoded keys anywhere. To restart a service: systemctl --user restart <service-name>.

cdg-ops.js
cdg/cdg-ops.js · service: cdg-ops.service
Purpose
  • Main orchestrator — ties together load fetching, Telegram bot, and approval flow
  • Spawns load-fetcher.js on a 10-minute polling loop
  • Handles Telegram YES/NO approval buttons
  • On YES: sends driver SMS via GHL, triggers broker call flow
  • 90-day API key rotation tracker (key-rotation.json)
Telegram Commands
  • /status — service health + last load cycle
  • /test — fire a mock load offer
  • /help — command list
  • /keys — show API key rotation status
  • /rotated — reset rotation clock after manual rotation
  • /stats — carrier count, load count, bot uptime
retell-webhook.js
cdg/retell-webhook.js · service: cdg-webhook.service · port: 3000
Purpose
  • HTTP server for all inbound webhooks — Caddy proxies /webhook/* → :3000
  • HMAC-SHA256 signature verification on Retell payloads
  • Sliding-window rate limiter (10 req/min per IP)
  • Security headers via Helmet, full audit log
Routes
  • POST /webhook/intake — carrier form submission → GHL contact + PandaDoc agreement + onboarding email
  • POST /webhook/retell — Retell post-call → load status update, broker DB write, GHL sync
  • POST /webhook/ghl-inbound — driver SMS reply → parse YES/NO → trigger broker call or decline
telegram-bot.js
cdg/telegram-bot.js · loaded by cdg-ops.js
Purpose
  • Telegram bot for Taz — load approval cards with inline YES/NO buttons
  • Only responds to Taz's chat ID (1930024743)
  • Approval flow: card shows load details → YES triggers driver SMS → NO declines silently
  • Handles all /status, /test, /help, /keys, /rotated, /stats commands
load-fetcher.js
cdg/load-fetcher.js · called by cdg-ops.js every 10 min
Purpose
  • Polls load board API for matching loads (mock mode until LOADBOARD_API_KEY set)
  • Filters by carrier home base, equipment type, deadhead cap (<50mi), min rate floor
  • Rate floor = max(carrier min_rate_per_mile, $2.50/mi)
  • Matching load → sends Telegram approval card to Taz
  • handleApproval() — on YES: sends GHL SMS to driver, starts 10-min window
GHL Custom Fields Read
  • contact.min_rate_per_mile — carrier rate floor
  • contact.equipment_type — truck type
  • contact.home_base — origin city/state
db.js
cdg/db.js · SQLite via better-sqlite3
Tables
  • loads — id, carrier_id, broker_phone, origin, dest, rate, status, timestamps
  • calls — id, load_id, retell_call_id, outcome, duration, transcript_url
  • brokers — id, phone, mc_number, avg_rate_pm, rejection_rate, reliability_score, blacklisted
  • lane_rates — carrier_id, broker_id, origin_state, dest_state, posted_rate_pm, negotiated_rate_pm, load_date
Notes
  • WAL mode + prepared statements — no raw query strings
  • lane_rates is proprietary data — compounds from load #1, cannot be rebuilt retroactively
send-agreement.js
cdg/send-agreement.js · called from intake handler
Purpose
  • Auto-sends Dispatcher-Carrier Agreement via PandaDoc API on every intake submission
  • Founding member logic: first 50 carriers → $49/mo locked, 51+ → $79/mo standard
  • Counter state in carrier-counter.json
  • Template tokens: carrier_name, company_name, mc_number, dot_number, carrier_address, carrier_phone, carrier_email, carrier_title, platform_fee, founding_note
Status
  • Currently using PandaDoc sandbox key — swap to production key ~13 days after trial converts
  • Template ID: DxygNFkBA5K6zyi3G4yubd
send-onboarding.js
cdg/send-onboarding.js · called from intake handler
Purpose
  • Sends onboarding email to new carrier after intake form submission
  • Uses Google OAuth (service account) to send from dispatch@clydedispatch.com
  • Attaches Carrier_Onboarding_Packet.html content as email body
ghl-helper.js
cdg/ghl-helper.js · GHL API wrapper
Functions
  • getCarriers() — fetches all GHL contacts tagged as "carrier"
  • updateContactField(contactId, field, value) — updates a custom field on a contact
  • sendSMS(contactId, message) — sends SMS via GHL LC Phone
  • createContact(data) — creates new carrier contact, deduplicates by email
  • lookupByPhone(phone) — used by inbound call handler for broker/carrier ID
Environment Variables
cdg/.env · chmod 600 · also in Bitwarden (CDG folder)
GHL_API_KEYGoHighLevel CDG Builder API key
GHL_LOCATION_IDGHL sub-account location ID
GHL_WEBHOOK_TOKENX-CDG-Webhook-Token for /webhook/ghl-inbound
RETELL_API_KEYRetell AI bearer token
RETELL_WEBHOOK_SECRETHMAC secret for Retell payload verification
RETELL_SKIP_SIG_VERIFYtrue (intentional — disable before production)
PANDADOC_API_KEYPandaDoc sandbox key (swap to prod ~13 days)
PANDADOC_TEMPLATE_IDDxygNFkBA5K6zyi3G4yubd
TELEGRAM_BOT_TOKENClydeDispatch_bot token
TELEGRAM_CHAT_ID1930024743 (Taz only)
WEBHOOK_BASE_URLhttps://clydedispatch.com
LOADBOARD_API_KEYNot set — mock mode active
Common VPS Commands
SSH: ssh Taz@100.88.255.43
Service Control
systemctl --user status cdg-ops systemctl --user restart cdg-ops systemctl --user restart cdg-webhook journalctl --user -u cdg-ops -f -n 50
Logs & Status
tail -20 ~/.openclaw/workspace/nova_activity.log cat ~/.openclaw/workspace/cdg/key-rotation.json cat ~/.openclaw/workspace/cdg/carrier-counter.json
Session Start Checklist
ssh Taz@100.88.255.43 "tail -20 ~/.openclaw/workspace/nova_activity.log" ssh Taz@100.88.255.43 "python3 -c \"import json; d=json.load(open('/home/Taz/.openclaw/workspace/build_queue.json')); [print(i.get('type'), i['id']) for i in d['queue'] if i.get('status')=='queued']\""