Data Flow — End to End
Load Board
123LB (mock)
123LB (mock)
→
load-fetcher.js
polls every 10 min
polls every 10 min
→
Telegram
YES / NO buttons
YES / NO buttons
→
cdg-ops.js
handleApproval()
handleApproval()
→
GHL SMS
driver offer
driver offer
→
Driver SMS Reply
YES within 10 min
YES within 10 min
→
retell-webhook.js
/webhook/ghl-inbound
/webhook/ghl-inbound
→
Retell AI
Clyde agent
Clyde agent
→
Broker Phone Call
outbound negotiation
outbound negotiation
Post-Call Webhook
→
retell-webhook.js
/webhook/retell
/webhook/retell
→
GHL Contact Update
load_status field
load_status field
→
SQLite DB
loads, calls, brokers
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 emailPOST /webhook/retell— Retell post-call → load status update, broker DB write, GHL syncPOST /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 floorcontact.equipment_type— truck typecontact.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, timestampscalls— id, load_id, retell_call_id, outcome, duration, transcript_urlbrokers— id, phone, mc_number, avg_rate_pm, rejection_rate, reliability_score, blacklistedlane_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 contactsendSMS(contactId, message)— sends SMS via GHL LC PhonecreateContact(data)— creates new carrier contact, deduplicates by emaillookupByPhone(phone)— used by inbound call handler for broker/carrier ID
Environment Variables
cdg/.env · chmod 600 · also in Bitwarden (CDG folder)
| GHL_API_KEY | GoHighLevel CDG Builder API key |
| GHL_LOCATION_ID | GHL sub-account location ID |
| GHL_WEBHOOK_TOKEN | X-CDG-Webhook-Token for /webhook/ghl-inbound |
| RETELL_API_KEY | Retell AI bearer token |
| RETELL_WEBHOOK_SECRET | HMAC secret for Retell payload verification |
| RETELL_SKIP_SIG_VERIFY | true (intentional — disable before production) |
| PANDADOC_API_KEY | PandaDoc sandbox key (swap to prod ~13 days) |
| PANDADOC_TEMPLATE_ID | DxygNFkBA5K6zyi3G4yubd |
| TELEGRAM_BOT_TOKEN | ClydeDispatch_bot token |
| TELEGRAM_CHAT_ID | 1930024743 (Taz only) |
| WEBHOOK_BASE_URL | https://clydedispatch.com |
| LOADBOARD_API_KEY | Not 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']\""