Skip to content

HappyFox to Puzzel Case Management Migration: Technical Guide

Technical guide to migrating from HappyFox to Puzzel Case Management. Covers API constraints, data mapping, conversation history preservation, and ETL architecture.

Nachi Nachi · · 27 min read
HappyFox to Puzzel Case Management Migration: Technical Guide
TALK TO AN ENGINEER

Planning a migration?

Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.

Schedule a free call
  • 1,500+ migrations completed
  • Zero downtime guaranteed
  • Transparent, fixed pricing
  • Project success responsibility
  • Post-migration support included

HappyFox to Puzzel Case Management Migration: Technical Guide

Info

TL;DR: HappyFox → Puzzel Case Management Migration

A HappyFox to Puzzel Case Management migration is a data-model redesign, not a CSV copy job. HappyFox's native CSV export strips staff replies, client replies, and private notes — you only get the initial message. You must use the HappyFox REST API (/api/1.1/json/tickets/) to extract full thread data. On the Puzzel side, tickets are created through API ticket channels with Basic Token or OAuth authentication. HappyFox Categories, Contact Groups, and Custom Fields do not map 1:1 to Puzzel's Teams, Categories, Forms, and Form Fields — each requires structural mapping decisions. Smart Rules, SLAs, and Canned Actions cannot be migrated programmatically. A typical mid-size migration (10,000–50,000 tickets) takes 2–4 weeks, broken down roughly as: 3–4 days planning and mapping, 3–5 days extraction, 3–5 days transformation and test loading, 2–3 days production loading and delta sync, and 2–3 days validation and cutover.

What Is a HappyFox to Puzzel Case Management Migration?

A HappyFox to Puzzel Case Management migration moves tickets, contacts, contact groups, custom fields, tags, and attachments from HappyFox Help Desk into Puzzel Case Management while preserving conversation history, timestamps, and relational integrity.

Puzzel Case Management is a Case/Ticket management tool that provides your contact centre or helpdesk with the ability to manage written interactions in a secure and efficient way. Each email in the queue is converted into a support ticket and assigned a unique case/ticket ID which is then filtered, categorised and distributed to the right team or agent. It is part of the broader Puzzel CX ecosystem — designed primarily for contact centres that need unified ticketing across email, SMS, and API integrations.

Teams move from HappyFox to Puzzel for three main reasons:

  1. Contact centre consolidation. Puzzel Case Management can be combined with the Puzzel Contact Centre to offer a unified platform for ticketing and case management integrated into the contact centre solution. Teams already running Puzzel for voice want a single-pane agent experience.
  2. European data residency. Puzzel is ISO27001 certified (by DnV). Organisations in the Nordics or broader EU that need data sovereignty benefit from Puzzel's European-hosted infrastructure.
  3. Skill-based routing. Puzzel efficiently handles incoming support requests from email, SMS, and API integrations by categorising, prioritising, and routing them to agents based on their most relevant skills.

This is not a lift-and-shift job. The two platforms have fundamentally different data models, and treating the migration as a CSV exercise will result in lost conversation threads and broken relationships.

Core Data Model Differences: HappyFox vs Puzzel

Before writing a single line of migration code, understand where the data models diverge:

Concept HappyFox Puzzel Case Management
Ticket container Category (hierarchical) Team + Category (flat, separate concepts)
Customer grouping Contact Group (with tagged domains) Organisation (optional dimension)
Custom data on tickets Ticket Custom Fields (text, dropdown, date, checkbox) Forms + Form Fields (pre-defined field sets per ticket type)
Custom data on contacts Contact Custom Fields Organisation Custom Attributes
Agent grouping Teams (within Categories) Teams (standalone, mapped to queues)
Automation Smart Rules, Canned Actions Inbound Rules, Event Rules, Outbound Integrations
SLA tracking Built-in SLA policies SLA analysis via reporting
Knowledge base Built-in KB with sections/articles Customer Hub (additional paid feature)
Ticket linking Ticket merging, parent tickets Parent/child ticket relationships
Ticket statuses New, In Progress, On Hold, Resolved, Closed (customisable) Open, Pending, Solved, Closed (configurable per instance)
API authentication API Key + Auth Code (Basic Auth) Basic Token or OAuth Token

Puzzel Case Management uses placeholders like {{organisation:name}}, {{category}}, {{form:name}}, and {{form_field:$FORM_FIELD_NAME}} to reference ticket metadata — a fundamentally different structure from HappyFox's flat custom field model.

Warning

HappyFox "Categories" and Puzzel "Categories" are not the same concept. In HappyFox, a Category is the primary routing container (like a department). In Puzzel, Categories are metadata labels on tickets, while Teams handle routing. This naming collision causes mapping errors if you don't catch it early.

HappyFox also lets one contact belong to multiple contact groups and uses a primary contact group to default ticket affiliation. Puzzel uses customer and organisation records, where organisation is an optional dimension. A many-to-many contact-to-group pattern in HappyFox often must be flattened into a primary organisation in Puzzel, with secondary affiliations stored in custom attributes or an external reference system.

Migration Approaches

1. Native CSV Export → Manual Import

How it works: Export tickets from HappyFox via Reports → Exports. Import contacts into Puzzel via CSV.

When to use it: Only for sub-1,000 ticket archives where conversation history is expendable.

HappyFox's export includes the client's initial message and subject of the email. However, you will not be able to export the staff/client replies that are updated after the initial message and private notes of a ticket.

Puzzel's CSV import covers customers and address books, not threaded ticket history. CSV is useful for seeding reference data, not replaying case conversations.

  • Pros: Zero engineering effort, fast for small datasets
  • Cons: Loses all conversation history, no attachments, no relational data
  • Scalability: Small datasets only (<1,000 tickets)
  • Complexity: Low
  • Risk: High — data loss is guaranteed for any team that needs thread history

2. API-Based Migration

How it works: Extract full ticket data including all updates (replies, notes, attachments) via the HappyFox REST API. Transform the data to match Puzzel's schema. Load into Puzzel through its API ticket channel.

All HappyFox help-desk instances are shipped with a RESTful web service API that enables various operations, including ticket creation, ticket update submission, ticket and user listing. On the Puzzel side, 3rd party integrations are available via open REST-based APIs and an API is also available for customers to develop their own integrations.

  • Pros: Full conversation history preserved, handles attachments, scriptable, supports reruns and deltas
  • Cons: Requires custom development, must handle rate limits and pagination
  • Scalability: Mid-size to enterprise datasets with proper batching
  • Complexity: High
  • Risk: Medium — needs proper error handling and retry logic

3. Custom ETL Pipeline

How it works: Build a three-stage pipeline: Extract from HappyFox API → Transform (clean, map, reshape) in a staging database → Load into Puzzel API. Typically Python or Node.js with PostgreSQL or SQLite for intermediate storage and validation.

  • Pros: Full control over transformation logic, reusable for ongoing syncs, staging database enables rollback and audit trails
  • Cons: Highest engineering investment (80–160 hours), requires maintenance
  • Scalability: Best for 50,000+ ticket datasets or multi-system consolidations
  • Complexity: High
  • Risk: Low (if well-tested) — staging database provides a checkpoint for re-runs

4. Middleware Platforms (Zapier, Make)

How it works: Use Zapier or Make to connect HappyFox triggers/actions to Puzzel API endpoints. HappyFox supports webhooks and workflow HTTP actions. Puzzel documents outbound webhooks and JSONPath-based response mappings.

Middleware is useful for delta sync and coexistence during phased cutovers, not for bulk historical migration.

  • Pros: Low-code, fast setup for ongoing sync
  • Cons: Not designed for bulk historical migration, per-task pricing adds up fast, limited transformation capability, weak for ordered thread replay
  • Scalability: Low for historical data (>5,000 records)
  • Complexity: Low
  • Risk: High for one-time migrations — timeout and rate limit issues at scale

5. Managed Migration Service

How it works: A migration partner handles extraction, transformation, loading, and validation end-to-end using pre-built connectors and tested pipelines.

  • Pros: Zero internal engineering burden, handles edge cases, includes validation and rollback planning
  • Cons: External cost, requires vendor coordination
  • Scalability: Any dataset size
  • Complexity: Low (for your team)
  • Risk: Low — accountability sits with the partner

Migration Approach Comparison

Approach History Preserved Attachments Scale Complexity Estimated Effort Best For
CSV Export ❌ No ❌ No <1K tickets Low 2–4 hours Quick archival, contact seeding
API-Based ✅ Yes ✅ Yes 1K–50K High 40–100 eng-hours Engineering teams with capacity
Custom ETL ✅ Yes ✅ Yes 50K+ High 80–160 eng-hours Large, complex, audit-heavy datasets
Middleware ⚠️ Forward-only ⚠️ Limited Event-driven Low 8–20 hours setup Delta sync during coexistence
Managed Service ✅ Yes ✅ Yes Any Low (your side) 2–8 hours coordination Teams without bandwidth or zero-downtime needs

Recommendations by scenario:

  • Small team, <5K tickets, simple fields: API-based migration with a single script (40–60 hours)
  • Enterprise, 50K+ tickets, custom fields, zero downtime: Custom ETL pipeline or managed service
  • Already on Puzzel Contact Centre, need ongoing sync: Middleware for new tickets, API migration for historical
  • No engineering bandwidth: Managed migration service

When to Use a Managed Migration Service

Building an API migration script in-house often looks like a two-day project on a sprint board. In practice, it becomes a multi-week engineering distraction. Use a managed service when:

  • Your dataset exceeds 20,000 tickets with conversation history
  • You have custom fields that require complex transformation logic
  • You need zero downtime during cutover
  • Your team's engineering bandwidth is committed to product work
  • You have compliance requirements (GDPR, ISO27001) that demand audit trails

Hidden costs of DIY migration that teams consistently underestimate:

  • Attachment re-hosting: HappyFox attachment URLs have an expiry time of 5 minutes. You must download every attachment during extraction and re-upload to Puzzel. At scale (e.g., 50,000 tickets averaging 2 attachments each = 100,000 file operations), this alone can take 2–3 days of continuous processing.
  • Rate limit management: HappyFox has a global rate limiter for API requests. Users who send many requests in quick succession may see error responses with HTTP status code 429. HappyFox allows roughly 500 GETs and 300 POSTs per minute; a breach can lock you out for up to 10 minutes. (support.happyfox.com)
  • Retry logic and idempotency: Failed API calls need to be retried without creating duplicate tickets. If you migrate a ticket before the associated contact exists in Puzzel, the ticket becomes orphaned. A failed migration with 30,000 tickets and no idempotency safeguards typically costs 40–80 hours of rework to identify and fix duplicates, orphaned records, and ordering errors — plus 3–5 days of agent downtime while the data is corrected.
  • Timestamp handling: If you fail to preserve historical timestamps, every ticket appears as if created on migration day, destroying SLA reporting and making historical analysis meaningless.
  • Validation: Field-level comparison across 10,000+ records takes more time than the migration itself — expect 16–24 hours of engineer time for thorough validation of a 20K-ticket dataset.

Pre-Migration Planning

Before extracting a single record, complete this audit.

Data Inventory

  • Tickets: Total count, date range, status distribution (open, pending, closed)
  • Contacts: Total count, contact groups, custom fields in use, duplicate emails
  • Contact Groups: Count, tagged domains, membership overlap, primary group usage
  • Categories: Full hierarchy, active vs archived
  • Custom Fields: Ticket custom fields, contact custom fields, data types
  • Knowledge Base: Article count, sections, inline images, attachments
  • Attachments: Total volume (GB), file types, average size per ticket
  • Smart Rules / Automations: Document all — these must be rebuilt manually
  • SLAs: Document policies for manual recreation in Puzzel
  • Canned Actions: Export templates for rebuilding

What to Leave Behind

Not everything needs to move:

  • Spam and test tickets
  • Contacts with no ticket history
  • Archived categories with zero active tickets
  • Duplicate contacts (merge before migration)
  • Tickets older than your compliance retention window
  • Unused custom fields — do not migrate garbage data into a clean Puzzel instance

See our Help Desk Data Migration Playbook for detailed guidance on what to move and what to leave behind.

Migration Strategy

Strategy Description Best For Typical Duration
Big Bang Full cutover in a single maintenance window <20K tickets, weekend migration 1–2 days
Phased Migrate by category/team over multiple windows Large orgs with independent departments 2–4 weeks
Incremental Migrate historical data first, then sync delta Zero-downtime requirements 1–3 weeks

We recommend a Big Bang cutover preceded by a delta migration strategy. Migrate the bulk of historical data first, then run a final delta pass to catch records created or modified during the initial extraction.

Migration Timeline Breakdown (Mid-Size: 10K–50K Tickets)

Phase Duration Key Deliverables
Planning & Mapping 3–4 days Data inventory, field mapping document, Puzzel instance configuration
Extraction 3–5 days Full HappyFox API export, attachment downloads, staging DB populated
Transformation & Test Load 3–5 days Data cleaned and reshaped, sandbox migration executed, mapping errors fixed
Production Load 2–3 days Production import, delta sync, attachment upload
Validation & Cutover 2–3 days Record count verification, UAT, email routing switch, monitoring
Total 2–4 weeks

Risk Mitigation

  • Take a full backup of HappyFox data before starting (API export to local storage)
  • Run at least two test migrations on a Puzzel sandbox (contact Puzzel support to provision a sandbox environment — this typically requires an active Puzzel license and a support request to enable a non-production instance)
  • Document a rollback plan: keep HappyFox active until validation passes
  • Assign a dedicated QA owner for post-migration validation

For a complete planning framework, see our help desk data migration checklist.

Data Mapping: HappyFox → Puzzel Case Management

This section makes or breaks the migration. Every object requires explicit mapping decisions.

Warning

If your brief mentions Accounts, Leads, Opportunities, or Pipelines, pause. HappyFox Help Desk is built around contacts, contact groups, tickets, categories, and custom fields. Puzzel's model is customers, organisations, ticket attributes, categories, forms, and form fields. Sales objects belong in a CRM, not in this migration.

Ticket Mapping

HappyFox Field Puzzel Case Management Field Notes
Ticket ID (display ID) Ticket Reference / external_reference_id Puzzel assigns its own IDs; store HappyFox ID in a custom field for traceability
Subject Subject Direct map
Category Team + Category Split: Team handles routing, Category handles classification
Status Status Map HappyFox statuses → Puzzel statuses (see status mapping table below)
Priority Priority Direct map (verify priority level names match)
Assignee Assigned User Map agent emails; agents must exist in Puzzel first
Tags Tags Direct map
Custom Fields Form Fields Requires creating matching Forms in Puzzel first
created_at created_at Preserve historical timestamp via API
text (initial message) Description HTML/Text

Status Mapping Reference

HappyFox Status Suggested Puzzel Status Notes
New Open Default initial state
In Progress Open Puzzel uses assignment + Open rather than a separate "In Progress"
On Hold Pending Indicates waiting on external input
Resolved Solved Agent considers issue resolved
Closed Closed Ticket fully closed
Custom statuses Map to nearest equivalent Document each custom status decision

Note: Puzzel status names are configurable per instance. Verify your instance's actual status values in Settings → Ticket Statuses before finalising the mapping.

Contact / Organisation Mapping

HappyFox Field Puzzel Case Management Field Notes
Contact Name Customer Name Direct map
Contact Email Customer Email Primary identifier for ticket association; normalize to lowercase
Contact Phone Customer Phone May need custom attribute in Puzzel
Contact Group Organisation A Puzzel customer can optionally use the dimension 'organisations'. If used, you will find the name and description for each organisation record.
Contact Custom Fields Organisation Custom Attributes Verify data type compatibility

Conversation Thread Mapping

HappyFox Field Puzzel Case Management Field Notes
Staff Reply Outbound message / Note In Puzzel, notes can be flagged as public or private. When a note has been marked as private, it will not be exposed when retrieving ticket content via an API channel.
Client Reply Inbound message Chronological order must be preserved
Private Note Private Note Must set private flag correctly
Attachments Attachments Re-upload required; HappyFox URLs expire in 5 minutes
Danger

Attachment URL Expiry: HappyFox attachment URLs expire after 5 minutes. Your extraction script must download each attachment immediately upon retrieval. Do not store URLs for later download — they will be dead links by the time you use them.

Custom Field → Form Field Mapping

HappyFox uses flat custom fields on tickets. In Puzzel, a ticket form is a set of predefined ticket fields that serves a specific support request. You need to:

  1. Group related HappyFox custom fields into logical Puzzel Forms (e.g., group "Order Number," "Product SKU," and "Purchase Date" into an "Order Support" form)
  2. Create those Forms in Puzzel admin before import — forms cannot be created via API
  3. Map each HappyFox custom field to a specific Form Field
  4. Handle data type conversions (HappyFox dropdown → Puzzel dropdown — verify that every option value matches exactly, or the API POST will fail silently or reject the record)

Migration Architecture: Extract → Transform → Load

Phase 1: Extract from HappyFox

The HappyFox API is a RESTful web service that supports JSON and multipart/form-data payload formats. Authentication uses HTTP Basic Auth with API Key and Auth Code.

Key extraction endpoints:

GET /api/1.1/json/tickets/?size=50&page=1          # Paginated ticket list
GET /api/1.1/json/ticket/<ticket_id>/               # Single ticket with updates
GET /api/1.1/json/users/?size=50&page=1              # Contact list
GET /api/1.1/json/contact_groups/                    # Contact groups
GET /api/1.1/json/categories/                        # Category list
GET /api/1.1/json/custom_fields/                     # Ticket custom fields

Extraction constraints:

  • Concurrent/parallel API calls to the ticket endpoint for any given particular ticket are not supported.
  • Page size maximum is 50 for ticket and report endpoints
  • Rate limiting returns HTTP 429 — implement exponential backoff
  • Attachment URLs inside ticket updates expire after 5 minutes — download immediately during extraction
  • EU-hosted HappyFox accounts use .happyfox.net — ensure your API base URL is correct
import requests
import time
import json
import os
 
HF_BASE = "https://yourcompany.happyfox.com"
HF_API_KEY = "your_api_key"
HF_AUTH_CODE = "your_auth_code"
 
def extract_tickets(page=1, size=50):
    """Extract tickets with full conversation history."""
    url = f"{HF_BASE}/api/1.1/json/tickets/"
    params = {"size": size, "page": page}
    response = requests.get(url, auth=(HF_API_KEY, HF_AUTH_CODE), params=params)
    
    if response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", 60))
        time.sleep(retry_after)
        return extract_tickets(page, size)
    
    response.raise_for_status()
    return response.json()
 
def extract_ticket_detail(ticket_id):
    """Get single ticket with all updates, attachments, and custom fields."""
    url = f"{HF_BASE}/api/1.1/json/ticket/{ticket_id}/"
    response = requests.get(url, auth=(HF_API_KEY, HF_AUTH_CODE))
    response.raise_for_status()
    return response.json()
 
def download_attachment(attachment_url, local_path):
    """Download attachment immediately — URLs expire in 5 minutes."""
    response = requests.get(attachment_url, stream=True)
    response.raise_for_status()
    os.makedirs(os.path.dirname(local_path), exist_ok=True)
    with open(local_path, 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)

Production scripts need significantly more error handling, logging, and asynchronous processing to handle attachments and relational dependencies at scale. The code above outlines the basic pagination and rate-limit handling pattern.

Phase 2: Transform

Store extracted data in a staging database before loading into Puzzel. This gives you a checkpoint for validation and re-runs without hitting the HappyFox API again.

Key transformations:

  1. Status mapping: Map HappyFox statuses to Puzzel equivalents using the translation dictionary defined in your mapping document
  2. Category → Team + Category split: Decide which HappyFox Categories become Puzzel Teams and which become Puzzel Categories
  3. Custom fields → Forms: Group HappyFox custom fields into Puzzel Form structures
  4. Contact Groups → Organisations: Map domain-tagged groups to Puzzel Organisations; flatten multi-group logic where needed
  5. Timestamp normalization: Ensure UTC consistency across all records; HappyFox returns timestamps in the account's configured timezone
  6. HTML sanitization: Clean ticket body HTML for Puzzel's editor compatibility — watch for inline images stored as base64 or authenticated URL references (see Edge Cases section)
  7. Deduplication: Merge duplicate contacts before import
  8. Legacy ID preservation: Store HappyFox IDs in immutable custom fields for traceability
STATUS_MAPPING = {
    "New": "Open",
    "In Progress": "Open",
    "On Hold": "Pending",
    "Resolved": "Solved",
    "Closed": "Closed",
}
 
def transform_ticket(hf_ticket, field_mapping):
    """Transform a HappyFox ticket into Puzzel-compatible format."""
    puzzel_ticket = {
        "subject": hf_ticket["subject"],
        "status": STATUS_MAPPING.get(hf_ticket["status"]["name"], "Open"),
        "priority": hf_ticket["priority"]["name"],
        "team": field_mapping["categories"].get(hf_ticket["category"]["id"]),
        "customer_email": hf_ticket["user"]["email"].lower().strip(),
        "tags": [tag["name"] for tag in hf_ticket.get("tags", [])],
        "created_at": hf_ticket["created_at"],
        "external_reference_id": str(hf_ticket["id"]),
        "custom_fields": {},
        "messages": []
    }
    
    # Map custom fields to Puzzel form fields
    for cf in hf_ticket.get("custom_fields", []):
        puzzel_field = field_mapping["custom_fields"].get(cf["name"])
        if puzzel_field:
            puzzel_ticket["custom_fields"][puzzel_field] = cf["value"]
    
    # Preserve conversation thread in chronological order
    for update in sorted(hf_ticket.get("updates", []), key=lambda u: u["timestamp"]):
        puzzel_ticket["messages"].append({
            "type": classify_message_type(update),
            "body": update.get("message", ""),
            "timestamp": update["timestamp"],
            "is_private": update.get("message_type") == "private_note",
            "attachments": extract_attachment_refs(update)
        })
    
    return puzzel_ticket

Phase 3: Load into Puzzel Case Management

In Puzzel, you create an API ticket channel by going to Settings → Ticket Channels → API, selecting an authentication level (Global or per-Organisation), and choosing a Token Type (Basic Token or OAuth Token).

Loading sequence matters. You must create dependencies before the records that reference them:

  1. Organisations — Create Puzzel Organisations from HappyFox Contact Groups
  2. Users/Agents — Provision agents in Puzzel ID (manual or SCIM API)
  3. Categories and Forms — Create via Puzzel admin interface (forms cannot be created programmatically)
  4. Customers — Create customer records with email, name, and organisation association
  5. Tickets — Create via API ticket channel with all metadata; override the default created_at timestamp with the historical value from HappyFox
  6. Ticket Messages — Add conversation thread entries in chronological order
  7. Attachments — Upload to each ticket/message

Puzzel API ticket creation example (pseudocode):

import requests
 
PUZZEL_BASE = "https://app.puzzel.com/ticketing/api/v1"  # Verify with your instance
PUZZEL_TOKEN = "your_basic_token_or_oauth_token"
 
def create_puzzel_ticket(puzzel_ticket):
    """Create a ticket via Puzzel API ticket channel.
    
    Note: Actual endpoint URL and payload schema depend on your 
    Puzzel instance configuration. Verify with Puzzel support.
    The API ticket channel must be created in Settings → Ticket Channels → API
    before this will work.
    """
    headers = {
        "Authorization": f"Bearer {PUZZEL_TOKEN}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "subject": puzzel_ticket["subject"],
        "status": puzzel_ticket["status"],
        "priority": puzzel_ticket["priority"],
        "team_id": puzzel_ticket["team"],
        "customer_email": puzzel_ticket["customer_email"],
        "external_reference_id": puzzel_ticket["external_reference_id"],
        "created_at": puzzel_ticket["created_at"],  # Historical timestamp
        "tags": puzzel_ticket["tags"],
        "form_fields": puzzel_ticket["custom_fields"],
        "description": puzzel_ticket["messages"][0]["body"] if puzzel_ticket["messages"] else ""
    }
    
    response = requests.post(
        f"{PUZZEL_BASE}/tickets",
        headers=headers,
        json=payload,
        timeout=30
    )
    
    if response.status_code == 429:
        retry_after = int(response.headers.get("Retry-After", 30))
        time.sleep(retry_after)
        return create_puzzel_ticket(puzzel_ticket)
    
    if response.status_code == 422:
        # Validation error — log payload for debugging
        log_error(f"Validation failed for ticket {puzzel_ticket['external_reference_id']}: {response.text}")
        return None
    
    response.raise_for_status()
    return response.json()  # Contains the new Puzzel ticket ID
 
def add_ticket_message(puzzel_ticket_id, message):
    """Add a conversation message to an existing Puzzel ticket."""
    headers = {
        "Authorization": f"Bearer {PUZZEL_TOKEN}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "body": message["body"],
        "is_private": message["is_private"],
        "created_at": message["timestamp"],
        "type": message["type"]  # "inbound" or "outbound" or "note"
    }
    
    response = requests.post(
        f"{PUZZEL_BASE}/tickets/{puzzel_ticket_id}/messages",
        headers=headers,
        json=payload,
        timeout=30
    )
    
    response.raise_for_status()
    return response.json()
Warning

Important: The Puzzel API endpoint URLs and payload schemas shown above are illustrative. Puzzel does not publish comprehensive public API documentation in the same way HappyFox does. You must: (1) create your API ticket channel in Settings → Ticket Channels → API, (2) contact Puzzel support to obtain your instance-specific API documentation and endpoint URLs, and (3) test against a sandbox before production use. The authentication method (Basic Token vs OAuth) and exact field names depend on your instance configuration.

Error handling patterns for the Load phase:

Error Cause Resolution
HTTP 429 Rate limit exceeded Exponential backoff; respect Retry-After header
HTTP 422 Validation failure (missing required field, invalid enum value) Log payload, fix mapping, retry
HTTP 404 Referenced team/category/customer doesn't exist Check dependency creation order; create missing dependency first
HTTP 409 Duplicate record (if idempotency key is set) Skip — record already exists
HTTP 500 Server error Retry with backoff; if persistent, contact Puzzel support
Timeout Large attachment or slow network Retry with longer timeout; chunk large files

Idempotency strategy: Use the HappyFox ticket ID stored in external_reference_id as an idempotency key. Before creating a ticket, query Puzzel for existing tickets with that reference. If found, skip creation and proceed to message/attachment sync.

Keep crosswalk tables for every dependency: group → organisation, contact → customer, ticket → case. Make loaders idempotent and serialize writes per ticket so thread order stays stable.

Warning

Puzzel API rate limits are not publicly documented as a fixed number. Contact Puzzel support before running a bulk migration to understand your instance's throughput limits and request a temporary increase. The 'Outbound Integrations', 'Event Rules', and 'Response Mappings' features require activation on your Puzzel Case Management instance before use. Confirm API access is enabled before development starts.

Delta Sync

Because the initial extraction takes time, tickets will be created or updated in HappyFox during the migration window. A delta sync captures only records modified since the initial extraction timestamp and updates Puzzel accordingly. This is the mechanism that allows a near-zero-downtime cutover:

  1. Run the bulk migration (historical data)
  2. Run the delta sync (records created/modified during bulk migration)
  3. Freeze HappyFox (set to read-only or disable inbound email)
  4. Run one final delta sync
  5. Validate record counts and spot-check data
  6. Switch mail routing to Puzzel

Use HappyFox's updated_after filter parameter on the tickets endpoint to retrieve only modified records:

GET /api/1.1/json/tickets/?size=50&page=1&updated_after=2024-01-15T12:00:00Z

Step-by-Step Migration Process

  1. Audit — Inventory all HappyFox data: tickets, contacts, groups, custom fields, KB articles, automations
  2. Map — Create a field-level mapping document (HappyFox → Puzzel) for every object type
  3. Configure Puzzel — Set up Teams, Categories, Forms, Form Fields, Organisations, and API ticket channels; provision agents; request API access and rate limit increase from Puzzel support
  4. Extract — Pull all data from HappyFox via REST API; download all attachments immediately
  5. Transform — Clean, reshape, and store in staging database
  6. Test Load — Import into Puzzel sandbox; validate record counts and field accuracy
  7. Fix — Correct mapping errors found in test; re-run
  8. Production Load — Import into production Puzzel instance in dependency order
  9. Delta Sync — Capture tickets created or updated since initial extraction; apply to Puzzel
  10. Validate — Record counts, field-level spot checks, UAT with agents
  11. Cutover — Freeze HappyFox (set to read-only), switch email routing to Puzzel
  12. Monitor — Watch for 2 weeks; keep HappyFox read-only as fallback

Edge Cases and Challenges

Inline Images

Warning

Beware of Inline Images HappyFox allows users to paste images directly into the ticket body. These are typically stored in one of three ways: (1) base64-encoded data URIs in the HTML, (2) authenticated URL references pointing to HappyFox's CDN, or (3) external image URLs. Types 1 and 2 require parsing and re-hosting: extract the image data, upload it as an attachment or to your own CDN, and replace the HTML src attribute with the new URL. Type 2 URLs will break after HappyFox deprovisioning, and type 1 URLs dramatically inflate ticket body size. If you migrate the raw HTML to Puzzel without parsing and re-hosting these images, they will appear as broken image icons to your agents.

Duplicate Contacts

HappyFox allows multiple contacts with the same email across different Contact Groups. Puzzel may not handle this gracefully. Deduplicate before import:

  • Choose the most recently active contact as the primary
  • Merge ticket histories onto the primary contact
  • Log all merge decisions for audit

Missing or Deactivated Authors

If a staff member has left the company, their HappyFox profile might be deactivated. When you migrate their historical ticket replies to Puzzel, the API may reject the interaction if the author ID does not correspond to an active Puzzel agent. Map deactivated HappyFox agents to a generic "Legacy Agent" profile in Puzzel to preserve the history. Create this profile before starting the migration, and add a note or custom field on affected messages indicating the original author name.

Multi-Level Ticket Relationships

Tickets in Puzzel can be linked together through a parent/child relationship. When done, such relations are shown in a dedicated table. If HappyFox tickets have parent/child or merged relationships:

  1. Import parent tickets first
  2. Record the new Puzzel ticket IDs in your crosswalk table
  3. Link child tickets to parent IDs in a second pass
  4. For merged tickets, decide whether to maintain the merge relationship or consolidate into a single ticket with all messages

Attachment Failures

  • Download from HappyFox immediately (5-minute URL expiry)
  • Store locally with ticket ID mapping (directory structure: ./attachments/{ticket_id}/{filename})
  • Re-upload to Puzzel after ticket creation
  • Verify file size limits — Puzzel rejects invalid Base64, restricted file types, and files exceeding size limits; large emails over 40MB can fail into ERROR status
  • Implement a retry queue for failed uploads — network interruptions during large file transfers are common

Multi-Group Contact Flattening

A HappyFox contact can belong to several contact groups with one designated as primary. Decide early which approach to take:

Approach Pros Cons
Flatten to primary organisation Simple, clean data Loses secondary affiliations
Create duplicate customer records Preserves all affiliations May cause confusion; tickets split across records
Store secondary affiliations in custom attributes Preserves data without duplication Requires custom field setup; not searchable as organisations

API Throttling

Both platforms enforce rate limits. HappyFox allows roughly 500 GETs and 300 POSTs per minute. If you hit HappyFox with too many concurrent requests, you will receive 429 errors and may be locked out for up to 10 minutes. Your migration script must implement exponential backoff, respect the Retry-After header, and log every throttled request for replay.

Limitations and Constraints

Puzzel-Side Constraints

  • No public bulk import API: Puzzel does not document a bulk ticket import endpoint. You create tickets one at a time through the API channel, which limits throughput. Expect approximately 1–3 tickets per second depending on message count and attachments per ticket.
  • Forms must pre-exist: Ticket forms are predefined sets of ticket fields. You cannot create forms programmatically — they must be configured in the admin UI before import.
  • Feature activation required: The 'Outbound Integrations', 'Event Rules', and 'Response Mappings' features require activation — contact Puzzel support and request enablement before development begins.
  • Organisations are optional: Not all Puzzel instances use Organisations. If yours doesn't, Contact Group data from HappyFox needs a different structure.
  • Knowledge base is separate: Customer Hub is an additional paid-for feature in Puzzel that can serve as a self-service portal. KB migration requires a separate workstream — see the Knowledge Base Migration section below.
  • API documentation is instance-specific: Unlike HappyFox's publicly documented API, Puzzel's API documentation may need to be obtained through support. Request it early in the planning phase.

HappyFox-Side Constraints

  • CSV export loses history: Only the initial message exports. Staff replies, client replies, and private notes require API extraction.
  • Attachment URLs expire in 5 minutes: Cannot batch-download later.
  • No parallel ticket-level API calls: Concurrent/parallel API calls to the ticket endpoint for any given particular ticket are not supported.
  • Global rate limiter: Exceeding the rate limit returns 429 errors and can lock out API access for up to 10 minutes.
  • EU-hosted accounts use .happyfox.net: Ensure your API base URL is correct.

Data Structure Compromises

  • HappyFox's flat custom fields become nested Form/Form Field structures in Puzzel — data reshaping is unavoidable
  • HappyFox ticket categories as routing containers have no direct Puzzel equivalent — you must split into Team + Category
  • Satisfaction survey data from HappyFox has no standard import path into Puzzel
  • Automations, macros, SLAs, and routing rules cannot be migrated programmatically — these must be manually documented and rebuilt in Puzzel using Inbound Rules, Event Rules, and response templates
  • Complex historical metrics (like time-in-status) rarely translate perfectly between vendor schemas, even when timestamps are backdated correctly

Knowledge Base Migration

If you use HappyFox's built-in KB and plan to move to Puzzel's Customer Hub:

  • Customer Hub is an additional paid-for feature — confirm licensing before planning this workstream
  • HappyFox KB articles are organized in Sections → Articles with rich HTML content
  • Extract articles via the HappyFox API: GET /api/1.1/json/kb/sections/ and GET /api/1.1/json/kb/articles/
  • HTML sanitization is critical: strip HappyFox-specific CSS classes, re-host inline images, convert internal links to new Customer Hub URLs
  • Customer Hub article creation may require manual configuration or a separate API — confirm with Puzzel support
  • Budget this as a separate 1–2 week workstream depending on article count

Validation and Testing

Record Count Comparison

Object HappyFox Count Puzzel Count Match?
Tickets _____ _____
Contacts/Customers _____ _____
Organisations _____ _____
Attachments _____ _____
Tags _____ _____
Categories/Teams _____ _____
Messages (total across all tickets) _____ _____

Field-Level Validation

  • Sample at least 5% of migrated tickets (minimum 50–100 records)
  • Compare: subject, status, priority, assignee, customer email, tags, custom fields
  • Verify conversation thread completeness: count messages per ticket in source vs target
  • For complex tickets (10+ replies, multiple attachments, custom fields), manually verify formatting, inline images, and chronological order in the Puzzel UI
  • Check attachment accessibility: open 10 random attachments in Puzzel
  • Confirm that Closed tickets in HappyFox remain Closed in Puzzel, and Open tickets remain Open
  • Verify that created_at timestamps match the historical values, not the migration date

UAT Process

  1. Select 3–5 agents from different teams
  2. Have each agent review 20 migrated tickets they previously handled
  3. Verify: correct conversation order, all notes present (including private notes), attachments accessible, inline images rendering
  4. Test: Can agents reply to migrated tickets? Do auto-replies work on the new channels?
  5. Document all discrepancies before production cutover

For a detailed post-migration QA process, see our Post-Migration QA Checklist.

Rollback Plan

  • Keep HappyFox active and read-only during validation — do not cancel the subscription
  • Document a cutover checklist with explicit go/no-go criteria
  • If validation fails: pause, fix transformation logic, re-run on sandbox
  • Set a hard deadline: if validation isn't complete within 5 business days, roll back to HappyFox

Post-Migration Tasks

Rebuild Automations

HappyFox Smart Rules, Canned Actions, and SLA policies cannot be exported. Rebuild them manually in Puzzel:

  • Inbound Rules: Replace HappyFox Smart Rules that auto-assign or auto-tag
  • Event Rules: Replicate escalation triggers
  • Response Templates: Recreate Canned Actions as pre-written response templates
  • SLA configuration: Set up SLA policies in Puzzel's reporting framework

Update Email Routing

Change your DNS and mail forwarding rules to route support@yourdomain.com to Puzzel instead of HappyFox. Steps:

  1. Update MX records or email forwarding rules to point to your Puzzel inbound email address
  2. Verify SPF/DKIM/DMARC records include Puzzel's sending domains
  3. Test inbound email delivery to Puzzel before deactivating HappyFox's email intake
  4. Monitor for bounce-backs during the first 24 hours

Agent Training

  • Puzzel's agent interface differs significantly from HappyFox — budget 2–4 hours of training per agent
  • Agents provisioned via Puzzel ID are created with standard 'Basic User' System Role credentials and no user signature initially applied. Configure signatures and roles before go-live.
  • Ensure your team understands the new terminology: "Cases" and "Teams" instead of "Tickets" and "Categories"
  • Test the Puzzel Contact Centre integration if your team uses both voice and ticketing

Monitoring

  • Track ticket creation rates in Puzzel for the first 2 weeks post-cutover
  • Monitor for orphaned tickets (tickets without customer association)
  • Check that inbound email routing is working correctly on the new Puzzel channels
  • Watch for missing attachments, stale search visibility, and unresolved mapping gaps
  • Verify CSAT survey configuration if applicable
  • Compare daily ticket volumes between the final week in HappyFox and the first week in Puzzel to ensure no emails are being lost

Best Practices

  1. Run a sandbox migration first. Execute a full test migration into a Puzzel sandbox. The first run always reveals mapping errors.
  2. Do not mutate source data. Treat HappyFox as a read-only data source. Never run scripts that alter HappyFox data during migration — you need a pristine fallback.
  3. Back up everything. Export all HappyFox data via API to local storage before every full run. This is your safety net.
  4. Validate incrementally. Check counts and sample records after each object type loads. Don't wait until the end to discover errors.
  5. Document every mapping decision. Maintain a living spreadsheet of HappyFox field → Puzzel field mappings. This becomes your audit trail.
  6. Coordinate with Puzzel support. Request API rate limit increases for migration windows. Confirm feature enablement and obtain API documentation before writing any code.
  7. Communicate the freeze. Give your support team a clear timeline for the freeze period where they stop working in HappyFox and switch to Puzzel.
  8. Use middleware for deltas, not for historical replay. Middleware handles forward-looking sync during a phased cutover. It does not handle years of threaded ticket history.
  9. Plan for the knowledge base separately. If you use HappyFox KB, Puzzel's Customer Hub is a separate product with its own migration considerations and costs.
  10. Test the full agent workflow post-migration. Don't just validate data — verify that agents can search, reply, reassign, and close migrated tickets in Puzzel without issues.

Making the Right Call

A HappyFox to Puzzel Case Management migration is a medium-complexity helpdesk migration. The biggest technical risk is conversation history loss — guaranteed if you rely on CSV exports. The biggest structural risk is the data model mismatch between HappyFox Categories and Puzzel's Team + Category + Form architecture.

Teams with fewer than 5,000 tickets and simple custom fields can self-serve with API scripting in 60–100 engineer-hours. For larger datasets, complex custom field mappings, or zero-downtime requirements, the engineering math tilts toward a managed migration.

If you're evaluating this migration and want to understand the exact scope for your dataset, we can review your HappyFox data model and give you a realistic timeline and risk assessment in a 30-minute call.

Frequently Asked Questions

Can I export conversation history from HappyFox via CSV?
No. HappyFox's native CSV export only includes the initial message and subject. Staff replies, client replies, and private notes are excluded. You must use the HappyFox REST API (/api/1.1/json/ticket//) to extract full conversation threads.
How long does a HappyFox to Puzzel Case Management migration take?
A standard migration with 10,000–50,000 tickets takes 2–4 weeks, including data mapping, test migrations, and validation. Smaller datasets under 5,000 tickets can be completed in 1–2 weeks with dedicated engineering effort.
Do HappyFox Categories map directly to Puzzel Categories?
No. Despite the shared name, they serve different purposes. HappyFox Categories are routing containers (like departments). Puzzel Categories are metadata labels on tickets, while Teams handle routing. You need to split each HappyFox Category into a Puzzel Team and one or more Puzzel Categories.
Can HappyFox Smart Rules and SLAs be migrated to Puzzel automatically?
No. HappyFox Smart Rules, SLA policies, Canned Actions, and satisfaction surveys cannot be exported or migrated programmatically. They must be manually documented and rebuilt in Puzzel using Inbound Rules, Event Rules, and response templates.
Does Puzzel Case Management have a bulk ticket import API?
Puzzel does not publicly document a dedicated bulk import endpoint. Tickets are created one at a time through API ticket channels. Contact Puzzel support to discuss throughput limits and request temporary rate limit increases for migration workloads.

More from our Blog

Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration
Help Desk

Post-Migration QA: 20 Tests to Run After Your Help Desk Data Migration

Ensure your help desk migration is a success with this comprehensive 20-point post-migration QA checklist. This expert guide details the 20 essential tests needed to validate your data integrity, system functionality, user-friendliness, and performance . Learn exactly how to check everything from ticket data, attachments, and knowledge base articles to critical workflows, automations, and integrations before you go live. This process is your final line of defense against lost tickets, broken workflows, and unhappy customers.

Raaj Raaj · · 8 min read