Documentation Index
Fetch the complete documentation index at: https://www.plain.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Using TypeScript? Check out our GraphQL SDK for a fully typed client.
importThread and importThreadMessages mutations let you bring across historic threads while preserving original timestamps, authors, and attachments so your team has full context in Plain.
Unlike createThread, imported threads do not trigger SLAs or autoresponders and are marked with import provenance tracking.
Overview
Importing a thread is a two-step process:- Create the thread with
importThread— this sets up the thread with its metadata (title, status, priority, labels, etc.) and the original creation timestamp. - Add messages with
importThreadMessages— this adds the conversation history (inbound messages, outbound replies, and internal notes) to the thread.
externalId, the duplicate is skipped (the result will be NOOP).
Permissions
To import threads you need an API key with the following permissions:thread:importattachment:create(if importing messages with attachments)
Import a thread
TheimportThread mutation creates a thread tied to an existing customer. You can identify the customer by their Plain customer ID, email address, or external ID.
The statusDetail.type must match the thread status:
TODOallowsNEW_REPLYorIN_PROGRESSSNOOZEDallowsWAITING_FOR_CUSTOMERDONEallowsDONE_MANUALLY_SETorIGNORED
tenantId, the customer must already be a member of the tenant or the import will fail.
Mutation
Variables
result field which is one of:
CREATED— the thread was imported successfully.NOOP— a thread with thisexternalIdalready exists, so the import was skipped.
Import thread messages
Once you have a thread, useimportThreadMessages to add conversation history. You can import up to 25 messages per call.
Each message has a type that determines which author field to set:
INBOUND— setauthor.customerId(the customer who sent the message)OUTBOUND— setauthor.userId(the support agent who replied)NOTE— setauthor.userId(the agent who wrote the internal note)
customerId or userId must be provided.
Mutation
Variables
results array with one entry per message in the same order as the input. Each result contains:
result—CREATEDorNOOP(if a message with thatexternalIdalready exists).threadMessage— the createdTimelineEntry(forINBOUND/OUTBOUND) orNote(forNOTE). Null if the message failed.error— per-message error details, null on success.
error will have the code bulk_partial_failure.
Importing messages with attachments
To import messages that have attachments, you need to upload the attachments first and then reference them by ID.Upload the attachment
Use
createAttachmentUploadUrl to get an upload URL, then upload the file. See the attachments guide for the full upload flow.When creating the upload URL, use the attachment type CUSTOM_TIMELINE_ENTRY for INBOUND and OUTBOUND messages, or NOTE for NOTE messages.Putting it all together
A typical migration script follows this order:- Upsert customers so they exist in Plain.
- Call
importThreadfor each ticket in the source system. - Upload any attachments using
createAttachmentUploadUrl. - Call
importThreadMessageswith the thread ID and messages (in batches of up to 25). - Check the
resultanderrorfields to confirm each import succeeded.
externalId, you can safely re-run a migration script without creating duplicates.
