Cleanbox
Features Helpdesk Blog Pricing Contact
Sign in Start free trial
api developer automation

How to Automate Incoming Helpdesk Emails with the Cleanbox API

If you run a support address through Cleanbox — whether as an alias or a relay address — you are sitting on structured data that can power your entire helpdesk workflow. Every incoming email has metadata, spam scores, contact categories, and attachments available through the API.

In this guide, we will build a Python script that polls your support alias for new messages, categorizes them by sender, extracts attachments, and creates tickets in your helpdesk system. The same pattern works for any webhook-less integration: CRM, project management, accounting, or custom dashboards.

What we are building

The workflow:

  1. Poll GET /v1/messages every 5 minutes for new delivered messages on the support alias
  2. For each new message, fetch the full details and spam report
  3. Check the contact's category and state to determine priority
  4. Extract attachments if present
  5. Create a ticket in your helpdesk (we will use a generic HTTP POST, adaptable to Zendesk, Freshdesk, Linear, etc.)

Prerequisites

  • A Cleanbox account with API access enabled (setup guide)
  • Python 3.8+ with the requests library
  • Your API key and the UUID of the support alias

Step 1: Set up the API client

First, create a simple wrapper for the Cleanbox API:

import requests
import time
from datetime import datetime, timezone

API_BASE = "https://api.cleanbox.app/v1"
API_KEY  = "your_api_key_here"

HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

def api_get(path, params=None):
    """Make a GET request to the Cleanbox API."""
    r = requests.get(f"{API_BASE}{path}", headers=HEADERS, params=params)
    r.raise_for_status()
    return r.json()

def api_post(url, data):
    """Make a POST request to an external service."""
    r = requests.post(url, json=data)
    r.raise_for_status()
    return r.json()

Note: we are not wrapping the entire API — just the calls we need. For a complete reference, see the API documentation.

Step 2: Fetch new messages

The messages endpoint supports filtering by address UUID and status. We want delivered messages on our support alias:

SUPPORT_ALIAS_UUID = "your-alias-uuid-here"

def get_new_messages(since_timestamp):
    """Fetch delivered messages on the support alias."""
    messages = []
    page = 1

    while True:
        data = api_get("/messages", {
            "address": SUPPORT_ALIAS_UUID,
            "status": "delivered",
            "page": page,
            "per_page": 100
        })

        for msg in data["data"]:
            # Only process messages newer than our checkpoint
            received = datetime.fromisoformat(msg["received_at"])
            if received > since_timestamp:
                messages.append(msg)

        if page >= data["total_pages"]:
            break
        page += 1

    return messages

The API returns messages sorted by date (newest first). We paginate through all pages and filter by our checkpoint timestamp. In production, you would store this timestamp in a file or database between runs.

Step 3: Enrich with contact data

Each message includes a contact UUID. We can fetch the contact to get their category and state:

def get_contact(contact_uuid):
    """Fetch contact details including category and state."""
    data = api_get(f"/contacts/{contact_uuid}")
    return data["contact"]

def determine_priority(contact):
    """Map contact state and category to ticket priority."""
    # Prioritized contacts = urgent
    if contact["state"] == "prioritized":
        return "urgent"

    # Whitelisted contacts = high (trusted, probably a client)
    if contact["state"] == "whitelisted":
        return "high"

    # Finance and Business Services categories = high
    category = contact.get("category", {})
    if category and category.get("name") in ["Finance", "Business Services"]:
        return "high"

    return "normal"

This is where Cleanbox's automatic contact categorization pays off. A message from a Finance-category sender automatically gets higher priority without any manual classification.

Step 4: Extract attachments

If the message has attachments, we can download them via the API:

import base64

def get_attachments(message_uuid):
    """Download message attachments as files."""
    data = api_get(f"/messages/{message_uuid}/attachments")
    files = []

    for att in data.get("attachments", []):
        files.append({
            "filename": att["filename"],
            "content_type": att["content_type"],
            "size": att["size"],
            "content": base64.b64decode(att["content"])
        })

    return files

The attachments endpoint returns base64-encoded content. We decode it to raw bytes, ready to attach to a helpdesk ticket or save to disk.

Alternatively, use GET /v1/messages/{uuid}/attachment/{index} to download a single attachment as a binary file (index starts at 1).

Step 5: Check the spam report

For support aliases, you might want to flag messages with suspicious spam signals even if they passed the threshold:

def get_spam_flags(message_uuid):
    """Check spam report for concerning signals."""
    data = api_get(f"/messages/{message_uuid}/spamreport")
    flags = []

    for rule in data.get("rules", []):
        if rule["rule"] in ["FORGED_SENDER", "SPOOF_DISPLAY_NAME", "PHISHING"]:
            flags.append(rule["rule"])

    return flags

If a message has FORGED_SENDER or PHISHING symbols, you might want to tag the ticket as suspicious even though it passed delivery.

Step 6: Create the ticket

Now we combine everything into a ticket creation function. This example uses a generic HTTP POST — adapt the payload to your helpdesk's API:

HELPDESK_API = "https://your-helpdesk.com/api/tickets"
HELPDESK_TOKEN = "your_helpdesk_api_key"

def create_ticket(message, contact, priority, attachments, spam_flags):
    """Create a ticket in your helpdesk system."""
    ticket = {
        "subject": message["subject"],
        "from_email": message["from_address"],
        "from_name": message["from_name"],
        "priority": priority,
        "category": contact.get("category", {}).get("name", "Uncategorized"),
        "tags": spam_flags,
        "received_at": message["received_at"],
        "cleanbox_message_uuid": message["uuid"]
    }

    # Add attachments if your helpdesk supports them
    if attachments:
        ticket["attachments"] = [
            {"filename": a["filename"], "size": a["size"]}
            for a in attachments
        ]

    print(f"Creating ticket: {ticket['subject']} [{priority}]")

    # Uncomment to actually create:
    # requests.post(HELPDESK_API,
    #     headers={"Authorization": f"Bearer {HELPDESK_TOKEN}"},
    #     json=ticket)

    return ticket

Step 7: The main loop

Tie it all together with a polling loop:

import os
from datetime import timedelta

CHECKPOINT_FILE = "last_check.txt"

def load_checkpoint():
    """Load the last check timestamp."""
    if os.path.exists(CHECKPOINT_FILE):
        with open(CHECKPOINT_FILE) as f:
            return datetime.fromisoformat(f.read().strip())
    # Default: check last 24 hours on first run
    return datetime.now(timezone.utc) - timedelta(hours=24)

def save_checkpoint(ts):
    """Save the current timestamp."""
    with open(CHECKPOINT_FILE, "w") as f:
        f.write(ts.isoformat())

def run():
    since = load_checkpoint()
    print(f"Checking for messages since {since.isoformat()}")

    messages = get_new_messages(since)
    print(f"Found {len(messages)} new messages")

    for msg in messages:
        # Get contact info
        contact = get_contact(msg.get("contact_uuid", ""))
            if msg.get("contact_uuid") else {}

        # Determine priority
        priority = determine_priority(contact)
            if contact else "normal"

        # Get attachments
        attachments = get_attachments(msg["uuid"])
            if msg.get("has_attachments") else []

        # Check spam signals
        spam_flags = get_spam_flags(msg["uuid"])

        # Create ticket
        create_ticket(msg, contact, priority, attachments, spam_flags)

    # Update checkpoint
    save_checkpoint(datetime.now(timezone.utc))
    print("Done")

if __name__ == "__main__":
    run()

Running it

For a simple setup, run the script via cron every 5 minutes:

*/5 * * * * cd /path/to/script && python3 helpdesk_sync.py >> /var/log/helpdesk_sync.log 2>&1

For a more robust setup, use a process manager (systemd, PM2) with a sleep loop, or trigger it via a webhook from your monitoring system.

What you can extend

  • Auto-reply: For known categories (e.g., "Billing" contacts), send an automatic acknowledgment email
  • Quarantine review: Use GET /v1/quarantine to also check quarantined messages that might be legitimate support requests caught by aggressive filtering
  • Contact auto-whitelist: When a ticket is resolved, use PUT /v1/contacts/{uuid} to set the contact to whitelisted so future emails from them skip spam checks
  • SLA monitoring: Track message timestamps vs. ticket creation time to measure response time

The Cleanbox API turns your email into structured data. What you build with that data is up to you.

Ready to take control of your inbox?

Start protecting your email with Cleanbox — free plan available, no credit card required.

Get started free