Skip to content
reaatechREAATECH

Files · Google Gemini Lead Intake for Jotform SMB Lead Qualification

67 (1 binary, 588.2 kB total)attempt 1

README.md·5612 B·markdown
markdown
# Google Gemini Lead Intake for Jotform SMB Lead Qualification
 
> Ingest form submissions from Jotform webhooks, classify lead quality with Gemini, auto-route hot leads to sales and ask clarifying questions for ambiguous entries.
 
A tutorialized reference solution from [reaatech.com](https://reaatech.com), demonstrating how to build production-grade AI systems with the `@reaatech/*` package family.
 
Small and medium businesses (SMBs) generate hundreds of web-form leads daily. Manually triaging every submission is slow, inconsistent, and costly. This recipe automates lead qualification: a Jotform webhook feeds submissions into a Gemini-powered classification pipeline that routes hot leads to Slack, sends clarifying emails for ambiguous entries, and silently drops noise — all with cost telemetry and session continuity built in.
 
## Architecture
 
```
Jotform Webhook → Express (HMAC verification) → ConfidenceRouter
    ├─ KeywordClassifier (fast pre-filter)
    └─ EmbeddingSimilarityClassifier (semantic scoring)
         └─ ConfidenceRouter.decide() → ROUTE / CLARIFY / FALLBACK
              ├─ ROUTE   → Slack bot notification to sales channel
              ├─ CLARIFY → Nodemailer email with follow-up questions
              └─ FALLBACK → Silent drop (logged with LLM cost telemetry)
```
 
## Quick start
 
```bash
git clone <repo-url>
cd google-gemini-lead-intake-for-jotform-smb-lead-qualification
pnpm install
cp .env.example .env
# Edit .env with your credentials (see Configuration below)
pnpm dev
```
 
## API reference
 
### `POST /api/webhook/jotform`
 
Receives a Jotform submission, authenticates via HMAC, classifies the lead, and triggers the appropriate action.
 
**Request**
 
- Method: `POST`
- Content-Type: `application/octet-stream` (raw body)
- Headers:
  - `x-jotform-signature` — HMAC-SHA256 of the raw body, keyed with `JOTFORM_WEBHOOK_SECRET`
 
Body (Jotform submission payload):
 
```json
{
  "formId": "123456",
  "submissionId": "654321",
  "ip": "203.0.113.1",
  "formTitle": "Contact Us",
  "answers": {
    "1": { "name": "name", "order": 1, "text": "John Doe" },
    "2": { "name": "email", "order": 2, "text": "john@example.com" },
    "3": { "name": "phone", "order": 3, "text": "+1-555-0100" },
    "4": { "name": "message", "order": 4, "text": "Interested in enterprise plan" }
  }
}
```
 
**Responses**
 
| Outcome | Status | Body |
|---|---|---|---|
| Any | `200` | `{ "handled": true, "outcome": "ROUTE"\|"CLARIFY"\|"FALLBACK", "classification": { "label": "general", "confidence": 0.9 } }` |
 
**Error codes**
 
| Status | Body | Meaning |
|---|---|---|
| `401` | `{ "error": "invalid_signature" }` | Webhook secret mismatch |
| `400` | `{ "error": "invalid_payload", "details": "..." }` | Malformed or missing required fields |
| `500` | `{ "error": "internal_error", "message": "..." }` | Unexpected server failure |
 
## Configuration
 
All environment variables are defined in `.env.example`. Copy to `.env` and fill in real values:
 
| Variable | Description |
|---|---|
| `NODE_ENV` | Runtime environment (`development`, `production`) |
| `PORT` | Express server port (default `3001`) |
| `JOTFORM_WEBHOOK_SECRET` | Shared secret for HMAC verification of incoming Jotform webhooks |
| `GEMINI_API_KEY` | Google AI API key for Gemini classification calls |
| `SLACK_BOT_TOKEN` | Slack bot token for posting messages |
| `SLACK_CHANNEL_ID` | Slack channel ID where hot leads are routed |
| `SMTP_HOST` | SMTP server hostname for sending clarification emails |
| `SMTP_PORT` | SMTP port (default `587`) |
| `SMTP_USER` | SMTP authentication username |
| `SMTP_PASS` | SMTP authentication password |
| `TRIGGER_SECRET_KEY` | Secret key for Trigger.dev webhook authentication |
| `TRIGGER_API_URL` | Trigger.dev API endpoint URL |
 
## Classification pipeline
 
1. **`KeywordClassifier`** — Fast pre-filter that scores the submission against a keyword dictionary. Low-confidence entries exit early as `FALLBACK` without invoking Gemini.
2. **`EmbeddingSimilarityClassifier`** — Runs Gemini embedding on the submission text and computes cosine similarity against reference lead profiles.
3. **`ConfidenceRouter.decide()`** — Combines both classifier scores into a final decision:
   - **`ROUTE`** — High confidence lead → Slack notification to sales channel.
   - **`CLARIFY`** — Medium confidence / missing critical fields → Nodemailer sends an email with clarifying questions.
   - **`FALLBACK`** — Low confidence → Silently dropped; logged via `llm-cost-telemetry`.
 
## REAA packages used
 
- [`@reaatech/confidence-router`](https://www.npmjs.com/package/@reaatech/confidence-router) — Core routing engine that combines classifier outputs into a final decision.
- [`@reaatech/confidence-router-classifiers`](https://www.npmjs.com/package/@reaatech/confidence-router-classifiers) — Keyword and embedding-similarity classifiers.
- [`@reaatech/structured-repair-core`](https://www.npmjs.com/package/@reaatech/structured-repair-core) — Repair of malformed LLM JSON output from Gemini classification.
- [`@reaatech/agent-handoff-routing`](https://www.npmjs.com/package/@reaatech/agent-handoff-routing) — Handoff routing for Slack and email channels.
- [`@reaatech/llm-cost-telemetry`](https://www.npmjs.com/package/@reaatech/llm-cost-telemetry) — Per-request cost tracking for Gemini API calls.
- [`@reaatech/session-continuity`](https://www.npmjs.com/package/@reaatech/session-continuity) — Maintains conversation context across multi-turn clarification flows.
 
## License
 
MIT — see [LICENSE](./LICENSE).