GEMINI LABJP
NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20NANOLITE — Nano Banana 2 Lite is here: Google's fastest and most cost-efficient Gemini Image model, made for running lightweight image generation cheaplyOMNIFLASH — Gemini Omni Flash is in public preview, a natively multimodal model that lets enterprises and developers build custom, dynamic video workflowsAGENTS — Managed Agents expand with background: true for async server-side runs and polling, remote MCP server integration, and refreshing credentials across interactionsMEMORY — The Memory Bank IngestEvents API is generally available, decoupling event ingestion from memory generation so you can stream content continuouslyTHROUGHPUT — Provisioned Throughput now lets you submit up to seven pending orders for the same model and regionDEPRECATE — Image generation models shut down on August 17, and the Grok 4.1 family on the Gemini Enterprise Agent Platform on August 20
Articles/API / SDK
API / SDK/2026-05-03Intermediate

Automate Contact Form Handling with Gemini API — Classification, Priority Scoring & Slack Alerts

Build a Python system that automatically classifies incoming contact form submissions using Gemini API, scores their priority, and sends structured Slack notifications — ready to deploy today.

gemini-api277python104automation51slack3structured-output22

If you run a web service or app solo, you already know the feeling: a batch of contact form submissions hits your inbox, and half your morning disappears sorting through them.

Is this a bug? A billing question? A feature request that's been asked five times before? Each message takes just a few minutes to triage — but those minutes add up fast when you're the only one handling support.

Gemini API changes that. The Python system below reads incoming inquiries, asks Gemini to classify them and assign a priority score, then posts a structured notification to Slack. No specialized infrastructure required.

How the System Works

The flow is straightforward:

  1. A contact form submission comes in (via email, webhook, or any trigger)
  2. Python sends the content to Gemini API
  3. Gemini returns a structured JSON with category, priority, summary, and suggested response time
  4. The result gets posted to your Slack channel with color-coded formatting

This works with whatever form tool you're using — Google Forms, Typeform, a custom contact page — as long as you can extract the subject and body text.

Prerequisites

Install the required packages:

pip install google-genai requests

Set your environment variables:

export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

Get your Gemini API key from Google AI Studio for free. For a refresher on getting started with the API, see Gemini API Quickstart. For Slack, create an Incoming Webhooks app in your workspace settings and copy the webhook URL for the channel you want notifications sent to.

Classifying Inquiries with Gemini API

Here's the core function that sends inquiry content to Gemini and gets back structured classification data:

import os
import json
from google import genai
from google.genai import types
 
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
 
def classify_inquiry(subject: str, body: str) -> dict:
    """
    Classify an inquiry and return a priority score.
 
    Returns:
        {
          "category": str,
          "priority": int (1-5),
          "priority_reason": str,
          "summary": str,
          "suggested_response_time": str
        }
    """
    prompt = f"""
Analyze the following support inquiry.
 
Subject: {subject}
Body: {body}
 
Return a JSON object with this exact structure:
{{
  "category": "bug_report | feature_request | usage_question | billing | other",
  "priority": integer from 1 to 5 (5 = highest urgency),
  "priority_reason": "one sentence explaining the priority score",
  "summary": "inquiry summary in 10 words or fewer",
  "suggested_response_time": "immediate | within 24 hours | within 3 business days | within 1 week"
}}
 
Priority guidelines:
- 5: Service-wide outage, data loss risk, security issue
- 4: Specific feature broken, billing problem
- 3: Performance issue, partial feature malfunction
- 2: Feature request, improvement suggestion
- 1: General usage question
"""
 
    response = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=prompt,
        config=types.GenerateContentConfig(
            response_mime_type="application/json",
            temperature=0.1,  # low temperature for consistent classification
        ),
    )
 
    return json.loads(response.text)

A note on temperature=0.1: classification tasks benefit from consistency over creativity. You want the same type of message to always get the same category. In practice, temperature=0 can occasionally prepend stray characters before the JSON — 0.1 has been more reliable in my testing.

Setting response_mime_type="application/json" tells Gemini to return raw JSON rather than wrapping it in a markdown code block. Without this, you'd need to strip the ```json delimiters before parsing. For a deeper look at structured output patterns, see the Gemini Structured Output Guide.

Sending Priority-Aware Slack Notifications

This function formats the classification result into a color-coded Slack attachment and sends it:

import requests
from datetime import datetime
 
def send_slack_notification(inquiry_data: dict, classification: dict) -> bool:
    """
    Post classification results to Slack with priority-aware formatting.
 
    Returns:
        True on success, raises on error
    """
    category_emoji = {
        "bug_report": "🐛",
        "feature_request": "✨",
        "usage_question": "❓",
        "billing": "💳",
        "other": "📝",
    }
 
    priority_colors = {
        5: "#FF0000",  # red — urgent
        4: "#FF6600",  # orange — high
        3: "#FFCC00",  # yellow — medium
        2: "#00CC00",  # green — low
        1: "#999999",  # gray — minimal
    }
 
    category = classification.get("category", "other")
    priority = classification.get("priority", 1)
    emoji = category_emoji.get(category, "📝")
    color = priority_colors.get(priority, "#999999")
 
    # Escalate header text for high-priority items
    if priority >= 5:
        header = "🚨 <!channel> URGENT: Immediate response needed"
    elif priority >= 4:
        header = f"⚠️ High-priority inquiry {emoji}"
    else:
        header = f"{emoji} New inquiry (priority {priority}/5)"
 
    payload = {
        "text": header,
        "attachments": [
            {
                "color": color,
                "fields": [
                    {
                        "title": "Summary",
                        "value": classification.get("summary", ""),
                        "short": False,
                    },
                    {
                        "title": "Category",
                        "value": category,
                        "short": True,
                    },
                    {
                        "title": "Priority",
                        "value": f"{'⭐' * priority} ({priority}/5)",
                        "short": True,
                    },
                    {
                        "title": "Suggested Response Time",
                        "value": classification.get("suggested_response_time", ""),
                        "short": True,
                    },
                    {
                        "title": "Reasoning",
                        "value": classification.get("priority_reason", ""),
                        "short": False,
                    },
                    {
                        "title": "From",
                        "value": inquiry_data.get("email", "unknown"),
                        "short": True,
                    },
                ],
                "footer": f"Received: {datetime.now().strftime('%Y-%m-%d %H:%M UTC')}",
            }
        ],
    }
 
    response = requests.post(
        os.environ["SLACK_WEBHOOK_URL"],
        json=payload,
        timeout=10,
    )
    response.raise_for_status()
    return True

Putting It All Together

The main handler ties classification and notification together, with proper error handling for production use:

def handle_inquiry(inquiry_data: dict) -> dict:
    """
    Accept raw inquiry data, classify it, and send a Slack notification.
 
    inquiry_data fields:
        subject: email subject or form field title
        body: full message content
        email: sender's email address
        name: sender's name (optional)
    """
    classification = None
 
    try:
        # Step 1: classify with Gemini
        classification = classify_inquiry(
            subject=inquiry_data.get("subject", ""),
            body=inquiry_data.get("body", ""),
        )
 
        # Step 2: notify Slack
        send_slack_notification(inquiry_data, classification)
 
        return {"status": "success", "classification": classification}
 
    except json.JSONDecodeError as e:
        # Gemini returned non-JSON — notify Slack with a fallback classification
        print(f"JSON parse error: {e}")
        fallback = {
            "category": "other",
            "priority": 3,
            "priority_reason": "Auto-classification failed — manual review needed",
            "summary": inquiry_data.get("subject", "")[:50],
            "suggested_response_time": "within 24 hours",
        }
        send_slack_notification(inquiry_data, fallback)
        return {"status": "fallback", "error": str(e)}
 
    except requests.exceptions.RequestException as e:
        # Classification succeeded but Slack failed
        print(f"Slack notification failed: {e}")
        return {
            "status": "classified_but_notify_failed",
            "classification": classification,
        }
 
    except Exception as e:
        print(f"Unexpected error: {e}")
        raise
 
# Quick test
if __name__ == "__main__":
    test_inquiry = {
        "subject": "Can't log in since this morning",
        "body": "I was using the app fine yesterday but now I'm getting an authentication error every time I try to log in. I haven't changed my password.",
        "email": "user@example.com",
        "name": "Jane Smith",
    }
 
    result = handle_inquiry(test_inquiry)
    print(result["classification"])
    # Expected: category=bug_report, priority=4, suggested_response_time=within 24 hours

What I've Learned Running This in Production

A few things stood out after running this for a couple of months:

The prompt wording matters more than you'd expect. The specificity of your priority guidelines directly affects classification consistency. Vague criteria like "high priority = urgent" produce inconsistent results. Concrete examples — "priority 5 = service-wide outage or data loss risk" — give Gemini a clear reference point.

Emotionally charged messages get slightly elevated priority. A furious bug report tends to score slightly higher than a calm one describing the same issue. This is actually reasonable behavior: a visibly frustrated user probably needs a faster response regardless of technical severity.

The cost is negligible. With Gemini 2.5 Flash, each classification request costs well under $0.01. For most indie projects handling under a few hundred inquiries per month, this is effectively free. For error handling strategies that keep costs predictable, see Gemini API Error Handling Guide.

Connecting It to Google Forms

If you use Google Forms, the simplest approach is to set up a Flask or FastAPI webhook endpoint and call it from an Apps Script trigger on form submission:

from flask import Flask, request, jsonify
 
app = Flask(__name__)
 
@app.route("/webhook/inquiry", methods=["POST"])
def inquiry_webhook():
    data = request.get_json()
    if not data:
        return jsonify({"error": "Invalid JSON"}), 400
    result = handle_inquiry(data)
    return jsonify(result)
 
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

Deploy this to Cloud Run and it only runs when a request comes in — no always-on server costs. Railway is another solid option if you prefer minimal configuration.

Next Steps

The biggest practical win from this system isn't speed — it's catching things that would have slipped through the cracks. Urgent bugs that arrive at 2am now surface in Slack instead of waiting until morning.

Start by running the test script with a few real inquiries from your existing backlog. Adjust the category definitions and priority thresholds to match your service's reality, then hook it up to your form. The full working code is above — copy, configure your environment variables, and you're ready to go.

Share

Thank You for Reading

Gemini Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API / SDK2026-03-20
Build an AI Data Analysis Agent with Gemini API — Combining Code Execution, Function Calling, and Structured Output
Learn how to build a production-ready AI data analysis agent in Python that combines Gemini API's Code Execution, Function Calling, and Structured Output to automatically analyze CSV/Excel data, generate visualizations, and produce structured reports.
API / SDK2026-07-13
When responseSchema Can't Do $ref: Handling Recursive Schemas in Production with responseJsonSchema
Gemini's responseSchema is an OpenAPI subset with no $ref or $defs, so it can't express shared definitions or recursion. Here's how I moved to responseJsonSchema to reuse localized fields and handle a recursive category tree in production.
API / SDK2026-07-05
Catching only the deprecations that touch you — feeding the official changelog to url-context
I found out an image model was being shut down three days before the deadline. Here is a deprecation radar that reads the official changelog through url-context and surfaces only the models I actually use, with working Python and the over-alerting tuning I had to do in production.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →