Importing tickets

Moving to Mantle Helpdesk from another support platform? You can import your existing tickets to preserve conversation history and keep everything in one place.

Import options

There are three ways to get your tickets into Mantle:

Intercom extension

If you're migrating from Intercom, use our dedicated extension to import your conversation history automatically. The extension handles contact matching, message threading, and preserves timestamps from your original conversations.

Help Scout extension

Migrating from Help Scout? Our Help Scout extension imports your tickets, conversations, and customer data with a few clicks.

Core API

For all other platforms—Zendesk, Freshdesk, custom systems, or anything else—use the Mantle Core API to import tickets programmatically. This gives you full control over the import process and works with any data source.


Importing via the API

The Core API lets you create tickets, add message history, and track status changes. Here's how to structure your import.

Authentication

All API requests require OAuth2 authentication with the following scopes:

  • read:tickets — for fetching ticket data

  • write:tickets — for creating and updating tickets

See Integrating with Mantle for authentication setup.

Step 1: Create the ticket

Create a ticket with the basic metadata. The contact will be automatically created or matched by email.

POST /api/core/v1/tickets

{
  "subject": "Payment processing issue",
  "status": "closed",
  "priority": "high",
  "tags": ["billing", "imported"],
  "contact": {
    "email": "[email protected]",
    "name": "Jane Customer"
  },
  "createdAt": "2024-01-10T09:00:00Z"
}

Required fields:

  • subject — The ticket subject line

Recommended fields:

  • contact — Object with email and optional name. If a contact with that email exists, it will be linked. Otherwise, a new contact is created.

  • contactId — Alternatively, pass an existing contact ID directly

Optional fields:

FieldDescription
statusOne of: open, pending, resolved, closed. Defaults to open.
priorityOne of: low, normal, high, urgent. Defaults to normal.
tagsArray of tag strings
customerIdLink to a Mantle customer
appIdLink to a specific app
assignedToIdAssign to a team member
createdAtOriginal creation timestamp (ISO-8601)
updatedAtWhen the ticket was last updated (ISO-8601)
lastMessageAtWhen the last message was sent (ISO-8601)
readOnlySet to true to mark as managed externally
managedByName of external system (e.g., "zendesk")

The response includes the created ticket with its id, which you'll need for adding messages.

Step 2: Import message history

Add the conversation history using the bulk messages endpoint. This replaces any existing messages on the ticket.

PUT /api/core/v1/tickets/{ticketId}/messages

[
  {
    "actorType": "customer",
    "content": "I can't process payments in my store.",
    "contentType": "text",
    "occurredAt": "2024-01-10T09:15:00Z",
    "contact": {
      "email": "[email protected]"
    }
  },
  {
    "actorType": "agent",
    "content": "Thanks for reaching out. I'm looking into this now.",
    "contentType": "text",
    "occurredAt": "2024-01-10T10:30:00Z",
    "agentId": "your-agent-uuid"
  },
  {
    "actorType": "customer",
    "content": "Any update on this?",
    "contentType": "text",
    "occurredAt": "2024-01-11T14:00:00Z",
    "contact": {
      "email": "[email protected]"
    }
  },
  {
    "actorType": "agent",
    "content": "Fixed! The payment gateway configuration was missing.",
    "contentType": "text",
    "occurredAt": "2024-01-12T15:30:00Z",
    "agentId": "your-agent-uuid"
  }
]

Message fields:

FieldDescription
actorTypeRequired. One of: customer, agent, system
contentRequired. The message text
contentTypeOne of: text, html, markdown. Defaults to text.
occurredAtOriginal timestamp (ISO-8601). Defaults to now.
contactRequired if actorType is customer. Object with email.
contactIdAlternative to contact object—pass existing contact ID
agentIdRequired if actorType is agent. The team member's ID.
isInternalSet to true for internal notes not visible to customer
attachmentsArray of attachment objects (see below)

Attachment format:

{
  "url": "https://example.com/file.pdf",
  "name": "invoice.pdf",
  "contentType": "application/pdf",
  "sizeBytes": 102400
}

Step 3: Import status history (optional)

If you want to preserve the history of status changes, assignments, and other events, use the events endpoint:

PUT /api/core/v1/tickets/{ticketId}/events

[
  {
    "type": "status_changed",
    "oldValue": { "status": "open" },
    "newValue": { "status": "pending" },
    "actorType": "agent",
    "agentId": "your-agent-uuid",
    "occurredAt": "2024-01-10T10:30:00Z"
  },
  {
    "type": "status_changed",
    "oldValue": { "status": "pending" },
    "newValue": { "status": "resolved" },
    "actorType": "agent",
    "agentId": "your-agent-uuid",
    "occurredAt": "2024-01-12T15:30:00Z"
  }
]

Contact matching

When you include a contact object with an email, Mantle automatically:

  1. Normalizes the email (trim whitespace, lowercase)

  2. Searches for an existing contact with that email

  3. Updates the contact if found, or creates a new one if not

This means you can import tickets multiple times without creating duplicate contacts—the same email always maps to the same contact.


Best practices

Preserve timestamps

Always include createdAt on tickets and occurredAt on messages. This keeps your historical data accurate and ensures tickets appear in the correct order.

Use tags for tracking

Add an "imported" tag or similar to easily identify which tickets came from your migration. This helps if you need to audit or clean up later.

Import in batches

For large imports, process tickets in batches of 50-100 to avoid rate limits and make it easier to resume if something fails.

Test with a small set first

Import 5-10 tickets first and verify they look correct before running the full migration. Check that:

  • Messages appear in the right order

  • Contacts are matched correctly

  • Timestamps are preserved

  • Attachments are accessible

Mark external tickets as read-only

If you're keeping the original system as the source of truth, set readOnly: true and managedBy: "your-system" on imported tickets. This prevents edits in Mantle and makes it clear the ticket is managed elsewhere.


API reference

For complete API documentation including all available fields and response formats, see the Core API reference.