---
title: "Exporting & Importing Help Desk Macros: CSV Template Guide"
slug: exporting-importing-help-desk-macros-csv-template-guide
date: 2026-08-12
author: Raaj
categories: [Migration Guide, Help Desk]
excerpt: "Learn how to export, translate, and import help desk macros with a universal CSV template. Covers Zendesk, Freshdesk, Intercom, Gorgias, and Help Scout."
tldr: "Help desk macros don't auto-migrate. Use a CSV template to export, map variables and actions, then import or rebuild on the target — with validation at every step."
canonical: https://clonepartner.com/blog/exporting-importing-help-desk-macros-csv-template-guide/
---

# Exporting & Importing Help Desk Macros: CSV Template Guide


Help desk macros are not portable data. They are platform-specific configuration — executable logic wrapped around a text payload. Every major platform (Zendesk, Freshdesk, Intercom, Help Scout, Gorgias) treats macros as native objects with proprietary variable syntax, action models, and visibility rules. No migration tool — including Help Desk Migration, Import2, or any native importer — automatically transfers macros between platforms.

If you approach macro migration as a copy-paste exercise, your agents will send raw template syntax like `{{ticket.requester.name}}` to live customers on day one.

This guide covers the exact CSV template, platform-specific export methods, variable translation logic, and import procedures to move macros between help desks without losing actions, visibility rules, or reply content. The CSV serves as your platform-neutral intermediate format — a staging layer for data cleaning, variable mapping, and audit trail — not as proof of portability.

For a broader look at why macros, automations, and workflows need their own migration workstream, see our guide on [migrating automations, macros, and workflows](https://clonepartner.com/blog/blog/how-to-migrate-automations-macros-workflows/).

## Use the Vendor's Actual Terminology

One avoidable time sink: searching for the wrong object name in the target platform's docs. Each vendor names the same concept differently, and their documentation won't surface results if you search for the wrong term.

| Platform | Term | Export Method | Native Import? |
|---|---|---|---|
| Zendesk | Macros | API (`/api/v2/macros.json`) or Swifteq app | No native bulk import |
| Freshdesk | Canned Responses | UI CSV export (Admin → Canned Responses) | Yes — CSV import |
| Intercom | Macros (formerly Saved Replies) | Settings CSV export or Macros API | No |
| Help Scout | Saved Replies | API only (Mailbox API) | No |
| Gorgias | Macros | CSV export from macros settings | Yes — CSV import |
| HubSpot | Snippets / Templates | — | — |

Teams lose hours searching for "macro import" in a system that calls the same asset a **canned response**, **saved reply**, or **snippet**. HubSpot splits macro-like functionality into **Snippets** (short reusable text blocks) and **Templates** (full email structures) — source macros with subject lines and full email bodies map to Templates; quick paragraphs map to Snippets.

## Why Macros Break During Migration

Macro failure modes fall into three categories:

1. **Variable syntax mismatch.** Every vendor uses a different templating language. Zendesk uses Liquid markup (`{{ticket.requester.first_name}}`). Freshdesk uses `{{ticket.contact.name}}`. Intercom uses `{{first_name}}`. Help Scout uses `{%customer.firstName%}` with optional fallback syntax like `{%customer.firstName,fallback=there%}`. Gorgias uses `{{ticket.customer.firstname}}`. These are not interchangeable strings — they need explicit mapping.

2. **Action payload incompatibility.** A macro that changes ticket status to "Pending" in one system will fail in a platform that doesn't have a "Pending" state. Zendesk macros bundle reply text with status, tags, priority, and assignee changes in a single action array. Freshdesk splits this: **canned responses** handle reply text, while **scenario automations** handle multi-action workflows. You need both features to replicate a Zendesk macro in Freshdesk. This is the mapping people most often get wrong.

3. **Structural limits.** CSV files can't store binary attachments. Native importers typically only accept text content, not action payloads. Vendor-specific HTML markup gets rejected or mangled by the target. If the target only supports reusable text (no ticket actions), split the migration into two deliverables: **reply content** and **operational actions**. Trying to hide both inside one CSV is how teams lose routing, tags, or status changes at cutover.

## The Universal Macro CSV Template

The CSV template creates a **platform-neutral intermediate format** that captures everything you need to reconstruct each macro on any target system. This is your staging ground for data cleaning, variable translation, and team review — not necessarily the file you import directly.

| Column | Type | Description |
| :--- | :--- | :--- |
| `macro_id` | String | Unique ID from the source. Used for deduplication and update matching. |
| `macro_name` | String | Display title agents see. Keep identical to the original for familiarity. |
| `description` | String | What the macro does. Don't skip this — it's your documentation. |
| `category` | String | Logical grouping (e.g., "Billing > Refunds"). Maps to folders in most platforms. |
| `reply_body_html` | HTML | Full HTML content of the reply. Preserves formatting for correct rendering. |
| `reply_body_plain` | Text | Plain-text fallback. Required by some import tools. |
| `action_status` | String | Ticket status the macro sets (e.g., `open`, `pending`, `solved`). |
| `action_priority` | String | Priority value (e.g., `low`, `normal`, `high`, `urgent`). |
| `action_assignee_group` | String | Group or team the ticket routes to. |
| `action_tags_add` | Semicolon-delimited | Tags applied when the macro fires. |
| `action_tags_remove` | Semicolon-delimited | Tags removed when the macro fires. |
| `attachment_urls` | Semicolon-delimited | Publicly accessible URLs for any file attachments. |
| `visibility` | String | `all_agents`, `specific_group`, or `personal`. |
| `active` | Boolean | Whether the macro is currently enabled. |
| `source_platform` | String | Tracks origin during cross-platform migrations. |
| `import_method` | String | `csv`, `api`, or `manual` — forces an explicit decision per macro. |
| `status` | String | Migration status: `ready`, `imported`, `tested`, `failed`. Prevents half-migrated macros from reaching production. |

> [!TIP]
> Use semicolons — not commas — as your delimiter for multi-value fields like tags. Commas inside CSV fields cause parsing failures in most spreadsheet tools unless you're disciplined about quoting.

> [!TIP]
> Always export both `reply_body_html` and `reply_body_plain`. Many help desks require HTML payloads via API to preserve line breaks and hyperlinks. If you only import plaintext, macros render as a single unreadable block.

## Step 1: Export Macros from the Source Platform

Every platform handles macro export differently. Here's what works, what doesn't, and where the gaps are.

### Zendesk

Zendesk has no native UI button to export macros. Two options:

**Option A: The Zendesk API.** Hit the `/api/v2/macros.json` endpoint. Each macro returns as a JSON object with an `actions` array containing field/value pairs:

```json
{
  "macro": {
    "id": 2534,
    "title": "Password Reset",
    "active": true,
    "actions": [
      { "field": "status", "value": "solved" },
      { "field": "comment_value_html", "value": "<p>Click here to reset: {{ticket.link}}</p>" }
    ]
  }
}
```

Extract with curl:

```bash
curl -s "https://YOUR_SUBDOMAIN.zendesk.com/api/v2/macros.json?per_page=100" \
  -u your_email/token:YOUR_API_TOKEN | python3 -m json.tool > macros_export.json
```

**Pagination:** Zendesk's offset pagination caps at 100 results per page and 10,000 total resources. For most macro libraries (typically under 500), this isn't an issue. For larger sets, use cursor-based pagination by passing `page [size]=100` and following the `links.next` URL. Rate limits are plan-based — pace your requests.

**Option B: Swifteq Macro Export app.** A free Zendesk Marketplace app that exports macros to CSV including metadata, actions, and dynamic content. Export-only — won't help with import.

Then transform the JSON to your CSV template with a script:

```python
import json, csv

with open('macros_export.json') as f:
    data = json.load(f)

with open('macros_template.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['macro_id','macro_name','description','category',
                     'reply_body_html','reply_body_plain','action_status',
                     'action_priority','action_assignee_group',
                     'action_tags_add','visibility','active','source_platform'])

    for macro in data.get('macros', []):
        actions = {a['field']: a['value'] for a in macro.get('actions', [])}
        comment = actions.get('comment_value_html', actions.get('comment_value', ''))
        writer.writerow([
            macro['id'], macro['title'], macro.get('description', ''),
            '', comment, '', actions.get('status', ''),
            actions.get('priority', ''), actions.get('group_id', ''),
            ';'.join(actions.get('current_tags', '').split(',')) if actions.get('current_tags') else '',
            'all_agents' if not macro.get('restriction') else 'specific_group',
            str(macro.get('active', True)).lower(), 'zendesk'
        ])
```

### Freshdesk

Freshdesk supports native CSV export. Navigate to **Admin Settings → Canned Responses**, select the responses, and click **Export Selected Responses**. The CSV is emailed to your admin address and the download link stays valid for 15 days. ([Freshdesk support](https://support.freshdesk.com/support/solutions/articles/50000004892))

> [!NOTE]
> The Freshdesk export CSV includes a `Canned Response ID` field. Do not change this ID when re-importing — Freshdesk uses it to match updates to existing responses. Remove the ID and Freshdesk creates a duplicate instead of updating.

The Freshdesk CSV includes columns for `Folder Name`, `Visibility`, and `Content HTML`. Map these to your universal template's `category`, `visibility`, and `reply_body_html` fields.

### Intercom

Intercom supports CSV export from **Settings**, providing one row per macro with Name, Visibility, Owner, timestamps, and Type. Only one content export can be in progress per teammate at a time, and exports are logged in the teammate activity log. ([Intercom docs](https://www.intercom.com/changes/en/151035-export-your-macro-content-to-csv))

One limitation: the CSV export captures metadata and usage stats, but for full reply body content you may need the Macros API.

### Gorgias

Gorgias exports macros as CSV from the macros settings area — straightforward with macro content, tags, and metadata. ([Gorgias docs](https://docs.gorgias.com/en-US/import-and-export-macros-207786))

### Help Scout

Help Scout has no native export for saved replies. Use the Mailbox API for programmatic extraction. Help Scout's own switching guide confirms that built-in help desk imports do not transfer rules, macros, or saved replies. ([Help Scout docs](https://docs.helpscout.com/article/870-a-scouts-guide-to-switching-help-desks))

## Step 2: Clean, Normalize, and Translate

This is the most critical phase — and where most errors enter the migration.

### Freeze the Raw Export

Keep the vendor file read-only. Add an export timestamp. Do all cleanup in a separate working sheet. This gives you a rollback point when something goes wrong.

### Variable Translation

Every platform uses different syntax for dynamic placeholders:

| Variable | Zendesk | Freshdesk | Intercom | Help Scout | Gorgias |
|---|---|---|---|---|---|
| Customer name | `{{ticket.requester.first_name}}` | `{{ticket.contact.name}}` | `{{first_name}}` | `{%customer.firstName%}` | `{{ticket.customer.firstname}}` |
| Agent name | `{{current_user.name}}` | `{{helpdesk.agent.name}}` | `{{teammate.first_name}}` | `{%user.firstName%}` | — |
| Ticket ID | `{{ticket.id}}` | `{{ticket.id}}` | `{{conversation_id}}` | `{%conversation.number%}` | `{{ticket.id}}` |

Build a find-and-replace map for every placeholder your macros use. Miss one, and agents send replies with raw template syntax to customers.

Run this programmatically — human error will break syntax:

```python
import pandas as pd

df = pd.read_csv('macros_export.csv')

variable_map = {
    r'\{\{ticket\.requester\.first_name\}\}': '{{ contact.firstname }}',
    r'\{\{ticket\.id\}\}': '{{ ticket.hs_ticket_id }}',
    r'\{\{current_user\.name\}\}': '{{ owner.fullName }}'
}

for source, target in variable_map.items():
    df['reply_body_html'] = df['reply_body_html'].str.replace(source, target, regex=True)
    df['reply_body_plain'] = df['reply_body_plain'].str.replace(source, target, regex=True)

df.to_csv('macros_translated.csv', index=False)
```

### Action Field Translation

Status values, priority levels, and ticket types have different names — and sometimes different concepts — across platforms.

| Action | Zendesk | Freshdesk | Intercom |
|---|---|---|---|
| Close ticket | `status: solved` | `status: resolved` | `state: closed` |
| Set high priority | `priority: high` | `priority: high` | `priority: priority` |
| Assign to group | `group_id: 12345` | `group_id: 67890` | `team: team-name` |

A `group_id` of `12345` from Zendesk means nothing in Freshdesk. Map all ID-based references to the target platform's actual IDs before import. Build a lookup table of source IDs → target IDs.

### HTML Sanitization

Reply body HTML will contain platform-specific markup. Strip vendor-specific CSS classes, `data-*` attributes, and wrapper `<div>` elements before importing. Most target platforms reject or mangle unfamiliar HTML.

```python
from bs4 import BeautifulSoup

def clean_html(raw_html):
    soup = BeautifulSoup(raw_html, 'html.parser')
    for tag in soup.find_all(True):
        tag.attrs = {k: v for k, v in tag.attrs.items()
                     if k in ['href', 'src', 'alt']}
    return str(soup)
```

## Step 3: Handle Attachments

Macros frequently include attachments — PDF forms, setup guides, calendar links. CSV files are text-based and cannot store binary files.

**To migrate macro attachments:**
1. Write a script to download attachments from the source system's API.
2. Upload files to a publicly accessible cloud storage bucket (AWS S3, Google Cloud Storage).
3. Record the new public URLs in the `attachment_urls` column of your CSV.
4. During import, the target system's API reads these URLs and ingests the files.

> [!WARNING]
> Do not rely on the source system's original attachment URLs. Once you shut down your old help desk, those links return 404 errors — breaking attachments in every migrated macro.

## Step 4: Import Macros to the Target Platform

Import capabilities vary wildly by platform. The target's constraints dictate your workflow.

### Zendesk

No native bulk import. Use the API to create macros one at a time via `POST /api/v2/macros.json`. For each CSV row, build a JSON payload and POST it:

```python
import requests, csv, time

SUBDOMAIN = 'your-subdomain'
EMAIL = 'admin@example.com'
TOKEN = 'your_api_token'

with open('macros_translated.csv') as f:
    reader = csv.DictReader(f)
    for row in reader:
        actions = []
        if row['action_status']:
            actions.append({'field': 'status', 'value': row['action_status']})
        if row['action_priority']:
            actions.append({'field': 'priority', 'value': row['action_priority']})
        if row['reply_body_html']:
            actions.append({'field': 'comment_value_html', 'value': row['reply_body_html']})
        if row['action_tags_add']:
            for tag in row['action_tags_add'].split(';'):
                actions.append({'field': 'current_tags', 'value': tag.strip()})

        payload = {'macro': {
            'title': row['macro_name'],
            'description': row.get('description', ''),
            'actions': actions,
            'active': row['active'] == 'true'
        }}

        resp = requests.post(
            f'https://{SUBDOMAIN}.zendesk.com/api/v2/macros.json',
            json=payload,
            auth=(f'{EMAIL}/token', TOKEN)
        )
        print(f"{row['macro_name']}: {resp.status_code}")
        time.sleep(0.5)  # respect rate limits
```

### Freshdesk

Native CSV import at **Admin → Canned Responses → Import**. Key constraints:

- Only `.csv` format
- Visibility must be `Available to all` or `Available to groups` — group names must match exactly
- Separate group names with double pipe symbols: `||`
- Folder name without visibility causes failure
- 100MB file size cap
- Freshdesk sends an error report after import — keep it with your project artifacts
- **Actions beyond reply text (status changes, tag additions) must be rebuilt separately as Freshdesk scenario automations**

### Gorgias

Accepts UTF-8 CSV with four exact lowercase headers: `name`, `body_text`, `tags`, and `id`. A blank `id` creates a new macro; a matching `id` updates the existing one; an unknown `id` skips the row. Tags must already exist in the target account. Formatting is not preserved when importing new macros. ([Gorgias docs](https://docs.gorgias.com/en-US/import-and-export-macros-207786))

Gorgias also offers a Zendesk importer that can pull active macros, but only reply text and status change actions transfer — unsupported actions arrive without mappings.

### Intercom

No CSV import for macros. Create them via the Macros API or rebuild manually in **Settings → Macros**. For teams with fewer than 50 macros, manual rebuild is often faster than scripting the API.

### Help Scout

No macro import capability — neither CSV nor bulk API. Use the Mailbox API's saved reply create endpoint for scripted rebuilds, or plan for manual creation. ([Help Scout API docs](https://developer.helpscout.com/mailbox-api/))

## Step 5: Validate the Import

A macro that imports without errors can still be broken. Validation must cover three layers:

1. **Count check.** Does the macro count on the target match your CSV row count? Simple, but catches silent failures.
2. **Content check.** Open 10–15 macros (especially the most-used ones) and verify reply text renders correctly. Look for broken HTML, raw placeholders, and missing formatting.
3. **Action check.** Apply each imported macro to a test ticket and confirm every action fires — status change, tag addition, group assignment. This is the step teams skip and regret.

> [!CAUTION]
> Never validate macros against live tickets. Create test tickets specifically for validation. One macro applied to a real customer ticket during testing can send an unintended reply and close an open issue.

### Pilot the Macros People Actually Use

Don't test random macros. Use whatever usage data the source platform provides — Zendesk supports usage sideloads on macro list responses, Freshdesk offers canned response analytics, Intercom exports usage by date range. Pick your pilot set based on operational impact:

- The top 10–20 most-used macros
- A few HTML-rich replies
- A few variable-heavy replies
- Each visibility type you use
- At least one action-heavy macro from the source

If those survive, the long tail is usually repetition.

## Common Failure Modes

**Macros import but actions don't fire.**
Cause: ID-based references (`group_id`, assignee IDs) from the source mean nothing in the target.
Fix: Build a source → target ID lookup table and translate before import.

**Reply text contains raw placeholder syntax.**
Cause: Variables weren't translated between platforms.
Fix: Run the placeholder mapping from Step 2 against every body cell before importing.

**Multi-paragraph replies collapse into a single line.**
Cause: CSV parser stripped line breaks during export. Intercom's export is known for splitting paragraphs into separate entries.
Fix: Use HTML bodies as the source of truth, not plain text. Wrap CSV cells containing HTML in double quotes and escape internal quotes.

**Visibility and permissions don't transfer.**
Cause: Group structures differ. "Tier 2 Support" in Zendesk has no meaning if Freshdesk calls that group "Level 2 Agents."
Fix: Map group names explicitly. Rename `visibility` and `action_assignee_group` values to match the target's exact group names.

**Dependency objects are missing.**
Cause: The target requires tags, groups, or custom fields to exist before macros can reference them. Gorgias requires existing tags on import. Freshdesk rejects invalid folder, visibility, and group combinations.
Fix: Create all dependency objects on the target before running the macro import.

## CSV vs. API vs. Manual Rebuild

| Macro Count | Recommended Method | Why |
|---|---|---|
| < 30 | Manual rebuild | Faster to re-create. Use the CSV as a reference document. |
| 30 – 200 | CSV export → script → API import | Worth automating. The scripting investment pays off. |
| 200+ | API-to-API with transformation layer | CSV becomes unwieldy at scale. Script the full pipeline. |

For any count, the CSV template still serves as your **single source of truth** — the document your team reviews, signs off on, and validates against. Even if you never import the CSV directly, it controls the migration.

If your macros rely on conditional logic (e.g., "If ticket priority is High, apply these tags; if Low, apply different tags"), a flat CSV can't capture branching behavior. In those cases, bypass the CSV and use a direct API-to-API script that extracts the full JSON, transforms the logic in memory, and pushes it to the target.

For a deeper dive into moving complex logic, see our [Workflow Preservation Checklist](https://clonepartner.com/blog/blog/workflow-preservation-checklist-conversion-reference/).

## The Real Cost of Macro Migration

The export is the easy part. The mapping and validation is where teams burn time. Budget **2–4 hours per 50 macros** for the full export-transform-import-validate cycle.

Treat your macros with the same care you treat your customer data. A perfect ticket migration is useless if your agents lose the tools they need to resolve those tickets.

For a full data mapping methodology with CSV examples for tickets, users, and organizations, see our [data mapping guide with CSV templates](https://clonepartner.com/blog/blog/your-ultimate-guide-to-data-mapping-for-a-flawless-help-desk-data-migration-with-csv-templates/).

> Need help migrating macros, automations, and workflows to your new help desk? ClonePartner handles the full export, mapping, and rebuild — so your team doesn't have to.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Can you export Zendesk macros to CSV?

Zendesk has no native CSV export for macros. Use the free Swifteq Macro Export app from the Zendesk Marketplace, or hit the /api/v2/macros.json API endpoint and convert the JSON response to CSV with a script.

### Do macros transfer automatically when migrating help desks?

No. Macros, canned responses, and saved replies are platform-specific configuration — not portable data. No migration tool transfers macros automatically. They must be exported, mapped, and recreated on the target platform.

### How do I import macros into Freshdesk from a CSV?

Freshdesk natively supports CSV import for canned responses. Go to Admin Settings → Canned Responses → Import and upload a CSV with Folder Name, Visibility, and Content HTML columns. Actions like status changes require separate scenario automations.

### Why do dynamic variables break after importing macros?

Every platform uses different syntax for dynamic data. Zendesk uses Liquid markup, Freshdesk has its own placeholders, and Help Scout uses a completely different format. You must run a programmatic find-and-replace on these variables before importing.

### What's the difference between Zendesk macros and Freshdesk canned responses?

Zendesk macros bundle a reply with multiple actions (status, tags, priority, assignee) in one click. Freshdesk splits this: canned responses handle reply text, while scenario automations handle multi-action workflows. You need both to replicate a Zendesk macro in Freshdesk.
