Launched:self-serve migrations intoSuperhuman Docs (Coda)
Try it now
01Agent-first
Runs where you already work
Plug it into Claude, ChatGPT or Cursor. Describe the move in plain English; the agent runs it.
02Engineer-led
Our production engine, unlocked
The pipeline our engineers use on managed enterprise migrations — the same code, now something you can drive yourself.
03Pricing
Try 10 pages free, then $1 a page
Credit-based, pay-as-you-go. No scoping call, no quote — sample it on your own docs before you spend anything.
04Sources
NotionSlabConfluenceSoonGoogle DocsSoon
Skip to content

Quip to Slack Canvas Migration: Comments, Annotations & @date

Quip inline annotations, thread messages, and @date mentions all degrade or disappear in a Slack Canvas migration. Here is what happens to each and what your real options are.

Abdul Wahab Abdul Wahab · · 17 min read
Quip to Slack Canvas Migration: Comments, Annotations & @date
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

Quip to Slack Canvas Migration: Comments, Annotations & @date

When you migrate Quip documents to Slack canvases, the document body transfers with reasonable fidelity. The comments do not. Quip has three distinct comment types — inline annotations, thread messages, and page-level comments — and each one degrades or disappears in the move to Slack Canvas. The @date mention, a Quip-native inline element, has no Canvas equivalent at all.

This guide covers what actually happens to each element, why inline annotations are structurally unrecoverable, what your realistic preservation options are, and what to do when extraction goes wrong mid-run.

Scope note: This guide covers Quip document comments. Quip spreadsheet cell comments have a different internal structure (anchored to cell coordinates rather than section IDs) and are treated as out of scope here — they warrant separate handling and are noted where behavior diverges.

Salesforce reports that roughly 65% of Quip documents are compatible with Slack Canvas, and even compatible documents may simplify formatting, flatten unsupported mentions to plain text, and alter comment structure. (help.salesforce.com)

The Structural Mismatch Between Quip and Slack Canvas

Quip and Slack Canvas operate on fundamentally different architectural models, and this mismatch is why comments and dates break during migration.

Quip uses a document-as-a-thread model. Every document, spreadsheet, or chat room is a thread object in the Quip API. Within a document, every paragraph, list item, and table cell is a distinct node with a unique section_id. When a user highlights text and leaves a comment, Quip creates an annotation object anchored to that specific section_id. This is a strong location model — comments have precise positional context. (quip.com)

Slack uses a canvas-as-a-file model. A Slack Canvas is a block-kit document stored as a file object. While you can leave comments on a Canvas through the UI, those comments are functionally threaded messages that sit beside the canvas, not inside it. The entire public Slack Canvas API surface consists of six methods: canvases.create, canvases.edit, canvases.delete, canvases.access.set, canvases.access.delete, and canvases.sections.lookup. None of these create or manage comments. (api.slack.com)

Warning

The core limitation: You cannot programmatically insert a comment into a Slack Canvas at a specific position. The Slack Canvas API has no comment-creation endpoint. This is not a limitation a custom script can overcome — it is a missing API capability on the Slack side.

The Three Quip Comment Types You Need to Distinguish

Quip's commenting model is more complex than most collaborative editors. A single Quip document thread can contain three structurally different kinds of comments, and they are stored and retrieved differently via the API. All three types come through the same Get Recent Messages endpoint, which returns 25 messages by default (max 100 per call) and requires pagination for full history. The presence or absence of an annotation field in the API response is the reliable programmatic discriminator between types.

Inline annotations

An inline annotation is a comment anchored to one or more specific section IDs within a Quip document. When a user highlights a paragraph or sentence and adds a comment, Quip stores that comment as a message with an annotation object containing an id and an array of highlighted_section_ids.

The API response includes this structure:

{
  "annotation": {
    "id": "string",
    "highlighted_section_ids": ["string"]
  }
}

This is the comment type that carries positional context — the reader can see exactly which text the comment refers to. It is also the most common and most valuable type, as it provides direct context for edits, approvals, and questions. Quip spreadsheet comments use cell coordinates rather than section IDs, so this structure does not apply to spreadsheet cells.

Thread messages

Thread messages are the chat-style conversation that runs alongside every Quip document. Every Quip document is a "thread," and that thread has a built-in message stream. These messages appear in the conversation pane, not anchored to any specific location in the document body. They include user messages, system events (like "Alice edited the document"), and status updates.

Thread messages are structurally the easiest to migrate. Because they apply to the whole document, they map cleanly to Slack's thread model — a Slack message thread attached to the Canvas.

The catch: authorship and timestamps. When writing via the Slack API, messages appear as posted by the migration bot at the time of migration, unless you use Slack's specialized import APIs or format the message blocks to explicitly state the original author and date (e.g., [2024-03-12] Alice: Looks good to me).

Page-level comments

Page-level comments are messages posted about the document as a whole rather than a specific section. They appear in the same message stream as thread messages but are user-authored feedback about the entire page. In the API response, they look like standard messages without an annotation field.

Like thread messages, page-level comments can be migrated to Slack without losing positional context, because there was no positional context to begin with. The primary challenge remains preserving the original timestamp and author identity.

Info

Naming trap: Quip's help content talks about inline comments and the conversation pane. Teams often say "page-level comment" for a comment targeting a whole block, image, or document area rather than a highlighted phrase. Treat that as a weakly anchored comment, not as the same thing as a conversation-pane message. The programmatic distinction: inline annotations have an annotation field; page-level and thread messages do not. (help.salesforce.com)

Why Inline Annotations Are Structurally Unrecoverable

Inline annotations are the hardest case in a Quip-to-Canvas migration — not because of a bug or rate limit, but because of an architectural mismatch that has no workaround.

The problem has two halves:

Half one: Quip section IDs do not survive export. Quip's Automation API outputs documents as HTML where each paragraph, list item, and table cell carries an id attribute corresponding to its internal section ID. But when you export that document — via the GUI (HTML, DOCX, PDF) or the Bulk Export API — those Quip-internal id attributes are stripped or become meaningless outside Quip. The annotation's highlighted_section_ids reference identifiers that no longer exist in the exported artifact.

Half two: Slack's Canvas API has no mechanism to anchor a comment to a position within a canvas. The canvases.sections.lookup method can help you find Slack section IDs for headings and text matching, but there is no API call to say "attach this comment to this canvas section." The operation does not exist. Canvas comments can only be created manually through the Slack UI by highlighting text and clicking "Add Comment." (api.slack.com)

The practical outcome: you can extract the annotation text, the author, the timestamp, and the raw highlighted_section_ids from Quip. But you cannot place that comment back onto the corresponding paragraph in a Slack Canvas. Once exported, the positional link is permanently severed.

Building a human-readable anchor map

The standard mitigation is to build an anchor map before exporting — a lookup table that translates each section_id to a human-readable location while Quip is still live. Here is a concrete example of what that mapping looks like:

Quip section_id Heading path Nearby text excerpt Comment author Comment text
ABC123 Q4 Plan > Revenue Projections "...total pipeline adjusted for..." alice@co.com "These numbers need updating post-board meeting."
DEF456 Q4 Plan > Risk Factors > Item 3 "...regulatory approval timeline..." bob@co.com "Legal confirmed this is still open."

To build this map programmatically: call GET /threads/{thread_id}?columns=html to retrieve the document HTML, parse each element with an id attribute, walk the DOM to reconstruct the heading hierarchy above each element, and extract a 100–150 character excerpt of surrounding text. Store the result as a structured JSON or CSV alongside your comment export.

A good pre-migration archive stores at minimum: the original Quip thread ID, the message/comment ID, the annotation section ID, any highlighted section IDs, the author identity, the timestamp, the heading path, and the nearby text excerpt. Without the heading path and excerpt, your archive tells you what was said but not where it was said.

What Happens to @date Mentions

Quip @date mentions degrade to static plain text during migration. Slack Canvas does not have a native @date mention type that can be created via API. The interactive behavior — clickable dates, reminders, notifications to mentioned users — is permanently lost. Any active reminders tied to those dates stop working immediately.

In Quip, typing @ followed by a date creates an interactive element that can trigger notifications, change color as the date approaches, and integrate with task lists. In the HTML export, this element renders as a <time> tag or a proprietary span containing a timestamp. When converted to Slack's block-kit format, the dynamic properties are stripped, leaving a static string like 2024-10-15.

Salesforce's conversion FAQ confirms that mentions not supported in Slack may become plain text. (help.salesforce.com)

The nuance: Slack canvases support dates entered natively in the Slack UI, and users can set reminders from those dates. But Slack's canvases.edit API lists supported @ mentions for users and channels only — not dates. A human can recreate a date in the Slack UI, but an automated migration pipeline cannot produce a Slack-native interactive date object via API. Hoping the converter will preserve the semantics is how teams end up with deadlines that still look readable but no longer notify anyone.

The safe pattern: preserve the visible date text, normalize ambiguous formats to ISO 8601 (YYYY-MM-DD) during extraction so dates are unambiguous, and document reminder behavior separately. If a date drives work, approvals, or escalation, recreate it deliberately in Slack after the document body lands.

Info

Salesforce Data Mentions and user identity are separate issues. Quip supports @mentioning Salesforce record fields inline (e.g., an Opportunity's close date). These are live-linked CRM data pulls and do not survive migration — they render as blank or static text in exports.

Quip also uses Salesforce user identities (User IDs, email addresses) which do not map automatically to Slack user IDs. A migration pipeline that preserves authorship must maintain an explicit Quip-user-ID → Slack-user-ID lookup table, built from your identity provider, HR system, or manual cross-reference. Without this, comment attribution will appear as email addresses or be attributed to the migration bot.

Practical Options for Preserving Comment History

There is no option that restores full fidelity. Every approach involves a trade-off between accessibility, context preservation, and engineering effort.

Option 1: Export the comment stream as a canvas appendix

Extract all comments using the Quip Get Recent Messages API, paginating to get full history. For each comment, capture the author, timestamp, text, and — for annotations — the heading path and text excerpt from your anchor map. Append a structured comment log to the bottom of the Slack Canvas using canvases.edit with an insert_at_end operation.

The appendix might look like:

## Comment Archive
 
**@alice** (2025-12-03, on: "Q4 Plan > Revenue Projections — ...total pipeline adjusted for...")
> These numbers need to be updated after the board meeting.
 
**@bob** (2025-12-04, page-level)
> Overall looks good, but check the formatting on the tables.

Pros: Comments are preserved in the same artifact as the document. Searchable in Slack. No separate system needed.

Cons: Inline positional context is text-based, not interactive. Long comment histories bloat the canvas. Slack Canvas content is limited to 1 MiB per document_content object.

Handling the 1 MiB limit: When a comment appendix approaches the size cap, use one of three strategies: (a) split the canvas into a primary document canvas and a linked "Comment Archive" canvas using canvases.create plus a reference link; (b) truncate to the most recent N comments by timestamp and note the cutoff date explicitly; or (c) move the full archive to a Slack-linked Google Doc or Confluence page and insert a single reference block in the canvas. Strategy (a) preserves the most data; strategy (b) is appropriate when only recent context matters; strategy (c) is best for compliance archives that need to remain searchable but are rarely accessed.

Option 2: Post comments as a channel thread

Instead of appending to the canvas, post the comment archive as messages in the Slack channel thread associated with the canvas. Use chat.postMessage with the thread_ts of the canvas's parent message.

To keep this manageable, batch comments into grouped block-kit messages rather than posting one message per comment. Slack's rate limit for chat.postMessage is 1 request per second per workspace at the base tier — at that rate, a document with 500 comments posted individually takes over 8 minutes and occupies the rate limit for the entire workspace. Batching 10–20 comments per message reduces this to 25–50 API calls.

Pros: Keeps the canvas body clean. Channel threads are a natural Slack pattern. Comments are searchable.

Cons: Comments are separated from the document. Harder to find months later. Retention follows Slack message policies, so this is not a substitute for a preserved source archive. (slack.com)

Option 3: Accept the loss and archive the Quip export

Export Quip documents as PDFs using the Bulk Export API and store those PDFs as your canonical record. Migrate only the document body to Slack Canvas for daily use, and point compliance or audit teams to the archived PDFs for comment history. According to Salesforce's Bulk Export documentation, PDF exports include conversation history (thread messages and page-level comments); inline annotations render in the PDF margin with their highlighted text visible. (help.salesforce.com)

Pros: Zero risk of comment data loss in the archive. PDF is a legally accepted format. Cleanest migration — zero clutter in Slack.

Cons: Two systems to maintain. Users must check a secondary system for historical context.

Tip

If you choose the archive route, capture the GET /threads/{id} JSON payload alongside the PDF export. The PDF serves human readers; the JSON serves eDiscovery tools and allows future programmatic processing if your compliance requirements change. Relying on just one format is a compliance risk.

Decision reference: comment type × migration option

Comment Type Appendix (Option 1) Channel Thread (Option 2) PDF Archive (Option 3)
Inline annotations Heading path + text excerpt included; positional anchoring lost Same as appendix but in thread form Preserved with visual margin position in PDF
Thread messages Appended chronologically; no anchoring needed Natural fit — thread messages become thread messages Included in bulk export PDF
Page-level comments Appended with "page-level" label Posted as top-level thread replies Included in bulk export PDF
@date mentions Degraded to plain text in canvas body Not applicable (body content, not comment) Rendered as static text in PDF
Spreadsheet cell comments Not covered by this guide Not covered by this guide May be included depending on export format

Decision tree: which option to choose

Does your Quip content have regulatory retention, legal hold, or audit requirements?
├── YES → Option 3 (PDF archive) is mandatory as baseline
│         ├── Do users need searchable comments in Slack daily? → Add Option 1 or 2
│         └── No daily access needed → Option 3 alone is sufficient
└── NO → Is comment history referenced regularly by active users?
          ├── YES → Is engineering capacity available for extraction scripting?
          │         ├── YES → Option 1 (appendix) for rich documents; Option 2 for lighter ones
          │         └── NO → Accept loss; migrate body only
          └── NO → Accept loss; migrate body only

Failure Modes and Partial Extraction

Rate limit and interruption errors are the most common cause of incomplete comment extraction. Understanding what gets silently dropped versus loudly errored prevents false confidence in your archive.

What the Quip API rate limit means in practice

The Quip API enforces a limit of 50 requests per minute per access token. Comment extraction requires at minimum two API calls per document: one GET /threads/{id} call for the document HTML (needed for the anchor map), and one or more GET /threads/{id}/messages calls for the comment stream (paginated at up to 100 messages per call).

For a 10,000-document workspace:

  • Anchor map extraction: 10,000 calls ÷ 50/min = 200 minutes minimum (3.3 hours), assuming no retries and no other API usage on the same token
  • Comment extraction (1 page per doc): another 200 minutes minimum
  • Comment extraction with pagination (avg. 3 pages per doc): ~600 minutes (10 hours)

In practice, add 30–50% for retries, error handling, and throttling backoff. A complete comment extraction for a 10,000-document workspace should be budgeted at 15–20 hours of continuous API runtime. Use a dedicated access token for migration work to avoid starving other Quip integrations during the extraction window.

Failure modes to monitor

Silent data loss: Pagination errors are the most dangerous failure mode because they appear to succeed while truncating data. If your pagination loop encounters a network timeout or a 429 rate-limit response mid-page and fails to retry, the next iteration may continue from the wrong cursor or restart from the beginning, producing a duplicate-but-incomplete record. Mitigation: log the cursor value and message count after every paginated call; compare the total retrieved against the thread.message_count field available in the thread metadata before closing the extraction for that document.

Loud errors (safe failures): HTTP 403 responses indicate the access token lacks permission to read that thread's messages. HTTP 404 responses indicate the document was deleted or moved. Both are loud — they will surface in your error log — and both mean the document should be flagged for manual review rather than silently skipped.

Partial anchor maps: If the document HTML call succeeds but the message call fails, you have an anchor map with no comments attached. This is preferable to the reverse (comments with no anchor map), but both states should be flagged. A complete record requires both the HTML parse result and the message extract for the same thread ID.

Recommended extraction pattern:

For each document:
  1. GET /threads/{id}?columns=html → store HTML, parse section IDs → anchor map
  2. GET /threads/{id} → read thread.message_count → store as expected_count
  3. Paginate GET /threads/{id}/messages until cursor exhausted
  4. Compare retrieved_count to expected_count
  5. IF retrieved_count < expected_count → flag for retry/manual review
  6. Mark document as COMPLETE only when both steps succeed and counts match

Track document status (PENDING, IN_PROGRESS, COMPLETE, FAILED) in a persistent store, not in memory. A migration run that crashes at 6,000 documents should resume at document 6,001, not restart from zero.

When Compliance or Audit Requirements Change the Answer

Regulated industries (finance, healthcare, legal): If your Quip comment history includes discussions about compliance decisions, customer data handling, or audit-relevant approvals, Option 3 is mandatory as a baseline. Layer Option 1 or 2 on top for day-to-day usability, but the PDF and JSON export are your legal backstop.

SOC 2 or ISO 27001 obligations: If your documentation process is part of an auditable control, you need to demonstrate that decision history was preserved during migration. An appendix (Option 1) creates a visible, searchable record within the destination system. Document the mapping methodology and keep a log of which Quip thread IDs map to which Canvas IDs — this mapping log is itself an audit artifact.

No regulatory constraints: If comments are informal collaboration artifacts with no retention requirements, accepting the loss is the pragmatic call. Spending engineering cycles reconstructing comment anchors for informal discussion threads is rarely worth it.

If auditors rely on inline comments to prove that peer reviews, approval workflows, or security sign-offs occurred — and those comments were anchored to specific paragraphs — losing the anchor degrades the audit trail. Document this fidelity loss explicitly for your compliance team before migration, not after.

On the Slack side, Enterprise Grid plans offer Discovery API coverage for canvas comments and version history, plus legal holds that can override retention. But Slack admins can also disable canvas version history and set retention policies that permanently delete canvases and comments. A compliance-safe plan starts by freezing the Quip record first, then converting — not the other way around. (slack.com)

Warning

Audit your Quip documents for legal holds before migrating. After the Quip subscription expires, data enters a wind-down period ending in permanent deletion. If any document is under legal hold, you must extract and archive it — including comments — before access is blocked. The API stops working once the blocked-login phase begins. A complete extraction for a large workspace can take 15–20 hours; do not start this process in the final week of your subscription.

Third-Party Migration Tooling

No third-party migration tool currently advertises full-fidelity Quip inline annotation transfer to Slack Canvas, because the target platform's API does not support it. However, several tools handle portions of the migration and may reduce scripting effort:

  • Egnyte and Cloudficient support Quip document body export and can target multiple destinations, but their Canvas-specific handling is limited to document content — comments are typically excluded or handled via separate archival workflows. Verify comment handling explicitly with the vendor before purchasing.
  • Spinbackup focuses on SaaS backup rather than migration; it can create a Quip snapshot archive (useful as an Option 3 baseline) but does not write to Slack Canvas.
  • Salesforce's native migration tool (accessed via the Slack admin panel for orgs with active Salesforce + Slack integration) handles document body conversion for the 65% of compatible documents but does not migrate comment history.

For custom comment extraction and anchor mapping, a purpose-built script using the Quip Automation API is currently the only approach that captures the full annotation structure. The extraction pattern described in the failure modes section above is the starting point for that script.

What to Do Before You Migrate

  1. Inventory your comment volume. Use the Quip Get Recent Messages API to sample representative documents. Identify which have annotations (check for the annotation field), which have long thread histories, and which use @date mentions. Check thread.message_count in the thread metadata to estimate extraction time before committing.

  2. Check for legal holds and retention policies. Any document under hold needs a PDF export regardless of your migration strategy.

  3. Build your identity map. Compile a Quip-user-ID to Slack-user-ID lookup table before extraction begins. Without it, comment authorship in the archive defaults to email addresses or migration bot attribution.

  4. Extract source data while Quip is live. Pull API HTML for section IDs, comment/message data, and PDFs with conversation history. Budget 15–20 hours of API runtime for a 10,000-document workspace. Do not leave this for the last week of your active subscription.

  5. Build the anchor map during extraction, not after. Parse Quip section IDs to heading paths and text excerpts in the same pass as the HTML download. Doing it as a second pass doubles API calls and doubles the time.

  6. Choose one preservation pattern per document class. Decide up front whether comments become an appendix, a Slack thread, or remain only in the archived Quip record. High-traffic documents with dense annotation history may warrant an appendix; low-comment documents can migrate body-only. Do not improvise per comment halfway through the run.

  7. Handle the 1 MiB canvas limit proactively. Flag documents where estimated comment volume may approach the limit before the migration run. Pre-decide whether to split, truncate, or externalize for each flagged document.

  8. QA with real stakeholders. Have document owners and compliance reviewers sign off on a sampled batch before broad rollout. Verify the chosen treatment is acceptable for each risk tier.

What This Means for Your Migration

The migration from Quip to Slack Canvas is a structural downgrade in document collaboration depth. Quip was built as a dedicated document platform with node-level architecture and a strong comment-anchoring model. Slack Canvas is a lightweight text surface attached to a chat application. Only about 65% of Quip documents are Canvas-compatible to begin with, and even those lose comment metadata, interactive mentions, and positional anchoring.

No script, middleware, or API workaround will force Slack Canvas to support inline annotations or dynamic @date mentions — the target API does not expose these capabilities. The most defensible migrations acknowledge this limitation before extraction begins: they build a complete, compliance-grade archive of the Quip instance (JSON + PDF), move only the living text to Slack Canvas, and treat the migration as a clean break rather than a perfect mirror.

If you only remember one thing: Quip comments are not just text — they are location, attribution, and workflow state. Slack Canvas can hold the document body and follow-on discussion, but it is not a one-to-one container for Quip's inline annotation model. Treat inline annotations as lossy, normalize @date to plain ISO 8601 text, resolve user identity before export, and choose your preservation strategy based on compliance burden — not on optimism about what the conversion tool will handle automatically.

Frequently Asked Questions

Can I migrate Quip inline comments directly into Slack Canvas?
No. Quip inline annotations depend on internal section IDs and highlighted ranges, while Slack's public Canvas API has no comment-creation or comment-anchoring endpoint. The positional link between an inline annotation and the text it references is structurally unrecoverable. You must extract comments separately via the Quip Get Recent Messages API and preserve them as an appendix, a channel thread, or a PDF archive.
What happens to Quip @date mentions during migration to Slack Canvas?
Quip @date mentions degrade to static plain text. Slack Canvas has no equivalent inline date-mention type — it supports @mentions for users and channels but not dates. The interactive behavior (clickable dates, reminders, notifications) is permanently lost. Active reminders stop working immediately upon migration. If a date drives deadlines or escalation, you must recreate it manually in Slack after the document body lands.
How should I preserve Quip comments for compliance or audit purposes?
For regulated content, export Quip documents as PDFs using the Bulk Export API, which includes comments in the PDF output. This provides a legally defensible archival record with visual positioning of inline annotations. Capture the JSON payload from GET /threads/{id} alongside the PDF — the PDF serves human readers, the JSON serves eDiscovery tools. Extract comments before the Quip read-only phase begins, as the API stops working once logins are blocked.
Does the Slack Canvas API support adding comments programmatically?
No. The Slack Canvas API consists of six methods: canvases.create, canvases.edit, canvases.delete, canvases.access.set, canvases.access.delete, and canvases.sections.lookup. None of these create or manage canvas comments. Comments can only be added manually through the Slack UI by highlighting text and clicking Add Comment.
Can I export Quip comments before moving to Slack?
Yes. Use the Quip Get Recent Messages API to extract all comments (inline annotations, thread messages, and page-level comments), paginating to retrieve full history. GUI export alone is not enough — Quip's GUI export does not include comments. The API rate limit is 50 requests per minute per token, so for large workspaces, comment extraction can take hours. Plan accordingly.

More from our Blog