Bloomfire to Slack Canvas Migration: API Limits & Data Mapping
Migrating Bloomfire to Slack Canvas means video becomes a transcript, Q&A loses accepted answers, and media-heavy teams lose their core UX. Here's what survives.
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
Bloomfire to Slack Canvas Migration: API Limits & Data Mapping
Migrating Bloomfire to Slack Canvas is a lossy conversion. Bloomfire is a multimedia knowledge platform where video, audio, and Q&A posts are first-class content types with automatic transcription, accepted-answer designation, comments, likes, and deep-indexed search. A Slack canvas is a markdown document — no embedded media player, no Q&A thread model, no like count. If your Bloomfire community is predominantly video and audio posts, you are not migrating a knowledge base; you are flattening it into text with file links on the side.
There is no native migration tool or turnkey connector between the two systems. To move data, you must extract posts and attachments via Bloomfire's REST API, upload media as raw files to Slack, and reconstruct the searchable text — primarily transcripts and Q&A threads — into Canvas markdown.
Before you start planning: if your real goal is Slack-side discovery, test integration before migration. Bloomfire already offers Slack integration for search, notifications, and routing Slack questions back into Bloomfire. Keeping Bloomfire as the system of record and surfacing it in Slack is often the safer architecture. Migrate only when you want Slack to become the durable home for the content and you accept flattening Bloomfire's richer behavior into documents plus files.
What Is a Bloomfire Post vs. a Slack Canvas?
A Bloomfire post is a content object that can be a rich-text article, a video with auto-generated transcript, an audio recording, or a Q&A thread where one answer is marked as accepted. Posts belong to categories and can be grouped into series (ordered playlists). They carry metadata including author, creation date, view count, likes, comments, and tags.
A Slack canvas is a markdown document that exists either as a standalone object or as a channel canvas tab. When created via the API, a canvas accepts a document_content object with type: "markdown" — that is the only supported content type. There is no embedded media player, no comment thread model inside the canvas body, and no metadata field for original author or creation timestamp (docs.slack.dev).
Slack Canvas markdown supports a specific subset of markdown syntax: headings (H1–H3), bold, italic, inline code, code blocks, unordered and ordered lists, blockquotes, and hyperlinks. Tables rendered in Canvas markdown display correctly but are not sortable or filterable. Embedded HTML is not supported — it must be stripped or converted before the canvas API call.
The gap between these two formats defines every trade-off in this migration.
What Happens to Video and Audio Posts?
Video and audio posts lose their playback experience entirely. A Slack canvas cannot play media inline — it is a text document. The migration path splits each Bloomfire media post into two artifacts:
- The media file is downloaded from Bloomfire and uploaded to Slack as a file using
files.getUploadURLExternalfollowed byfiles.completeUploadExternal. The file lives in a channel and can be linked from the canvas body as a Slack file URL that unfurls into a preview. - The transcript becomes the canvas content. For many teams, this is the more valuable artifact — the transcript is what people search for, quote in tickets, and reference in meetings.
The practical result: your team goes from clicking play on a 12-minute product walkthrough to reading a transcript with a download link to the original MP4. That is a real downgrade for visual content. It is an acceptable trade-off for content where the spoken words matter more than the visuals — sales call recordings, meeting notes, interview captures.
Transcripts may not be available through the API. Bloomfire's automatic transcription powers their deep-indexed search. The /api/v2/posts/{id} endpoint returns a body field containing rich-text HTML content. For video and audio posts, this field contains the post description entered by the author — not the auto-generated transcript. Auto-generated transcript text, if exposed at all, does not appear as a standard named field in the documented API response schema. Before committing to this migration path, make a direct API call against one of your video posts and inspect the full response payload for any transcript-related keys (common patterns to test: transcript, transcription, captions, full_text). If no transcript field is present, your options are: request a bulk data export from Bloomfire support, extract transcript text from the rendered post HTML using a headless browser, or re-transcribe extracted media files using a service like OpenAI Whisper (whisper-1 model via the Audio Transcriptions API at $0.006/minute as of 2025).
How Does Q&A Map to Canvas Markdown?
A Bloomfire Q&A post is a structured object: a question with one or more answers, where one answer can be marked as accepted. This accepted-answer designation tells readers which response was validated by the team or the original asker. The Q&A engine supports voting, per-answer comments, and certification workflows.
Slack canvas markdown has no native concept of Q&A threads, accepted answers, or voting. Your representation is purely convention-based:
## Q: How do we handle expired API tokens in production?
> **✅ Accepted Answer** (answered by Jane Smith)
> Rotate tokens using the /auth/refresh endpoint before the 72-hour expiry.
> Set a cron job at 48 hours to avoid edge-case failures during deployments.
> **Answer** (answered by Mike Chen)
> We previously used a manual rotation process but moved to automated
> refresh after the March incident.This is a formatting convention, not a data model. Nothing in Slack prevents someone from editing the canvas and moving the checkmark. No API field distinguishes accepted from non-accepted answers after migration. If your team relies heavily on the accepted-answer signal — particularly for compliance, support playbooks, or training certification — that signal degrades to a visual hint.
How Q&A fields map in the API response:
The Bloomfire API returns Q&A posts with "post_type": "question". Within the post object, the body field contains the question text. Answers appear as child objects — accessible via /api/v2/posts/{id}/contributions or embedded in the post response depending on your tenant version. The accepted answer is typically flagged with "accepted": true on the answer object. Extract this boolean and map it to the visual convention in your canvas markdown before that distinction is permanently lost.
Comments and likes do not migrate. Bloomfire posts carry comment threads and like counts that inform content quality signals. The Slack Canvas API does not provide a method to bulk-create native canvas comments programmatically. You can append a "Comments" section to the bottom of each canvas with the text of historical comments, but they will not be interactive — they are static markdown text attributed to the original author by name only. Most teams choose to drop comments and likes entirely, preserving only the core content and the accepted answer.
How Do Categories and Series Map to Channels?
Bloomfire categories are top-level taxonomy buckets like "Sales Enablement" or "Product Training." Bloomfire series are ordered sequences of posts, like a playlist or course. A single post can belong to multiple categories.
Slack Canvas has no folder concept. A channel holds exactly one channel canvas — a second conversations.canvases.create call for the same channel returns channel_canvas_already_exists. Standalone canvases are unlimited on paid plans but have no inherent hierarchy.
| Bloomfire Concept | Slack Target | Notes |
|---|---|---|
| Category | Slack channel | One channel per category (e.g., #kb-sales-enablement) |
| Series | Index canvas + linked standalone canvases | Channel canvas becomes a table of contents linking to individual post canvases |
| Individual post | Standalone canvas shared to channel | One canvas per post, shared into the relevant channel(s) |
| Cross-categorized post | Canvas shared to multiple channels | A single canvas can be shared to multiple channels |
The index canvas pattern. Since each channel gets exactly one channel canvas, use that canvas as a structured index — a table of contents for every migrated post in that category. Each entry links to a standalone canvas. This replicates Bloomfire's browse-by-category experience. For series, the index preserves sequence by listing posts in order with numbering. There is no native "playlist" or "next post" navigation in Slack.
If your Bloomfire workspace has deep hierarchies (e.g., Engineering > Frontend > React), you cannot recreate those folders in Slack. Two structural options exist:
- Flat Channel Model: Map the lowest-level category to a channel (e.g.,
#kb-eng-frontend-react). This pollutes the Slack sidebar with hundreds of single-purpose channels. - Index Canvas Model: Map the top-level category to a channel (
#engineering). Use that channel's primary canvas as an index linking to standalone canvases for subcategories and individual posts.
The Index Canvas model is the only scalable path. During migration, your script must first create the standalone canvases, capture their Slack URLs, then dynamically generate the index markdown before publishing it to the channel.
Free Slack plans cannot create standalone canvases and are limited to one canvas tab per channel. If your workspace is on a free plan, this migration pattern does not work — you would need one channel per post, which is not viable. Confirm your Slack plan supports unlimited standalone canvases before starting.
Extracting Data from the Bloomfire API
Authentication
Bloomfire's API uses OAuth 2.0 bearer tokens. The token request follows this pattern:
curl -X POST "https://{your-tenant}.bloomfire.com/api/v2/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}"The response returns an access_token with an expiry window. Include it in subsequent requests as Authorization: Bearer {access_token}. Some older Bloomfire deployments use API key authentication passed as a query parameter or header — validate your tenant's auth flow against the admin panel before writing the extractor, especially if you are inheriting legacy internal scripts. If your tenant uses API keys, the header format is typically X-Api-Key: {key} or a api_key query parameter.
Rate Limits
Bloomfire does not publish a universal rate limit in its public documentation. Based on integration experience, 200 requests per minute per token is the practical ceiling before the API returns 429 Too Many Requests. Treat this as a working estimate, not a guaranteed figure — benchmark your specific tenant before parallelizing. The 429 response includes a Retry-After header specifying wait time in seconds; implement exponential backoff against this value.
Pagination
Bloomfire's /api/v2/posts endpoint uses offset-based pagination with page and per_page parameters. Maximum per_page is typically 20 (verify against your tenant):
import requests
import time
def extract_all_posts(base_url, token, per_page=20):
posts = []
page = 1
headers = {"Authorization": f"Bearer {token}"}
while True:
resp = requests.get(
f"{base_url}/api/v2/posts",
headers=headers,
params={"page": page, "per_page": per_page, "status": "published"}
)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 30))
time.sleep(retry_after)
continue
resp.raise_for_status()
data = resp.json()
batch = data.get("posts", [])
if not batch:
break
posts.extend(batch)
page += 1
# Respect rate limit: ~200 RPM = ~3.3 RPS
time.sleep(0.35)
return postsThis pattern handles the 429 with Retry-After respect and includes conservative sleep between requests. For a 2,000-post community at 20 posts per page, this loop makes 100 list requests plus 2,000 detail requests — approximately 2,100 total API calls, which at 200 RPM takes roughly 11 minutes for metadata alone.
The Media Download Bottleneck
Large video and audio files must be streamed individually. File sizes for video commonly range from 50 MB to 2 GB per post. Bloomfire communities frequently host hour-long town halls, product demos, and training sessions.
If your extraction script attempts to load a 2 GB video into system RAM before writing to disk, the process will OOM and crash. Use chunked downloading to write files directly to a local staging environment or an intermediate cloud bucket:
def download_file_chunked(url, dest_path, headers, chunk_size=8 * 1024 * 1024):
with requests.get(url, headers=headers, stream=True) as r:
r.raise_for_status()
with open(dest_path, "wb") as f:
for chunk in r.iter_content(chunk_size=chunk_size):
if chunk:
f.write(chunk)For a community with 500 video posts averaging 200 MB each, you are looking at roughly 100 GB of media that must be downloaded from Bloomfire (constrained by API rate limits and bandwidth), temporarily stored on your migration infrastructure, and re-uploaded to Slack via files.getUploadURLExternal → files.completeUploadExternal. Budget 2–5 days for a media-heavy community's file transfer alone.
What the Extraction Returns
{
"id": 12345,
"title": "Q3 Product Roadmap Walkthrough",
"post_type": "video",
"body": "<p>In this session, VP of Product...</p>",
"author": {
"id": 678,
"name": "Sarah Johnson",
"email": "sarah@example.com"
},
"created_at": "2024-03-15T14:30:00Z",
"updated_at": "2025-01-11T09:03:00Z",
"categories": ["Product", "Leadership Updates"],
"tags": ["roadmap", "q3", "2024"],
"likes_count": 47,
"comments_count": 12
}The body field contains rich-text HTML. For video/audio posts, body contains the description the author entered — not the auto-generated transcript. The post_type field drives your transformation logic: "article" maps to direct markdown conversion, "question" requires the contributions endpoint for answers, "video" and "audio" require the separate media download path. Check the full response for any additional keys in your tenant before finalizing field mappings.
Writing to the Slack Canvas API
On the Slack side, creating canvases requires canvases:write scope for both bot and user tokens. Slack retired the legacy files.upload method on November 12, 2025 — use the current file pipeline (docs.slack.dev).
The relevant rate limits:
| Endpoint | Tier | Rate |
|---|---|---|
canvases.create |
Tier 2 | 20+ requests/min |
canvases.edit |
Tier 3 | 50+ requests/min |
files.getUploadURLExternal |
Tier 4 | 100+ requests/min |
For a migration of 1,000 posts, canvas creation alone takes a minimum of 50 minutes at maximum Tier 2 rate — more realistically 70–90 minutes with backoff and error handling. Each canvases.edit call supports only one operation (insert, replace, delete), so building a canvas that requires multiple sections means multiple API calls per canvas.
The markdown body is limited to 1 MiB (1,048,576 characters) per document_content object. This is generous — even a full video transcript rarely exceeds 500 KB — but verify against your longest posts. Long transcripts must be chunked into sections or split across multiple canvases.
Handling 429 from Slack's API:
import requests
import time
def create_canvas_with_backoff(token, title, markdown_body, channel_id=None, max_retries=5):
payload = {
"title": title,
"document_content": {
"type": "markdown",
"markdown": markdown_body
}
}
if channel_id:
payload["channel_id"] = channel_id
for attempt in range(max_retries):
resp = requests.post(
"https://slack.com/api/canvases.create",
headers={"Authorization": f"Bearer {token}"},
json=payload
)
data = resp.json()
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 60))
time.sleep(retry_after)
continue
if not data.get("ok"):
error = data.get("error", "unknown_error")
if error == "ratelimited":
time.sleep(60 * (2 ** attempt)) # exponential backoff
continue
raise RuntimeError(f"Slack API error: {error} | Response: {data}")
return data
raise RuntimeError(f"Exceeded max retries for canvas creation: {title}")The Slack API returns 200 OK for rate-limited responses when called with JSON bodies, placing the ratelimited error inside the response JSON rather than as an HTTP 429. Check data ["ok"] and data ["error"] in addition to the HTTP status code.
Uploading Media Files to Slack
To move Bloomfire videos into Slack, use the two-step upload flow:
def upload_file_to_slack(token, file_path, filename, channel_id):
file_size = os.path.getsize(file_path)
# Step 1: Get a signed upload URL
url_resp = requests.get(
"https://slack.com/api/files.getUploadURLExternal",
headers={"Authorization": f"Bearer {token}"},
params={"filename": filename, "length": file_size}
)
url_data = url_resp.json()
if not url_data["ok"]:
raise RuntimeError(f"Failed to get upload URL: {url_data['error']}")
upload_url = url_data["upload_url"]
file_id = url_data["file_id"]
# Step 2: POST file data to the signed URL
with open(file_path, "rb") as f:
upload_resp = requests.post(upload_url, data=f)
upload_resp.raise_for_status()
# Step 3: Finalize the upload
complete_resp = requests.post(
"https://slack.com/api/files.completeUploadExternal",
headers={"Authorization": f"Bearer {token}"},
json={
"files": [{"id": file_id, "title": filename}],
"channel_id": channel_id
}
)
complete_data = complete_resp.json()
if not complete_data["ok"]:
raise RuntimeError(f"Failed to complete upload: {complete_data['error']}")
return complete_data["files"][0]["permalink"]Once you have the Slack file permalink, inject that URL into the markdown payload for canvases.create. If the file exceeds Slack's native hosting limits, host the videos externally (AWS S3, Google Drive, or a dedicated video host like Vimeo) and migrate only the URLs into the canvas.
What Does Not Survive the Migration?
Be explicit with stakeholders about what is lost:
- Inline media playback. Videos and audio become downloadable Slack files, not embedded players.
- Auto-generated transcripts. Not exposed as a named field in the standard API response; test your tenant before committing.
- Video metadata. Duration, thumbnails, chapter markers, and in-video search timestamps do not transfer.
- Accepted-answer designation. The
"accepted": trueboolean on answer objects degrades to a visual convention (emoji or callout) with no programmatic meaning post-migration. - Comments. Can be appended as static text but lose interactivity, threading, and timestamps.
- Likes and view counts. No equivalent write surface in Slack Canvas. Lost entirely unless logged to a separate data store before migration.
- Historical timestamps. Canvas
created_atreflects migration time, not original post date. Embedding the original date in the canvas body makes it searchable but it will not appear in Slack's metadata or sort order. - Author attribution. Canvases created by a bot token are owned by the bot. Slack's API does not let you assign authorship to another user. Enterprise Grid workspaces with admin tokens and
canvases:write:userdelegation can partially address this, but the canvas still reflects the migration date, not the original creation date. Even with impersonation, you cannot backdate thecreated_atsystem field. - Search behavior. Bloomfire's deep-indexed search operates across file contents, video transcripts, and Q&A threads. Slack's native search indexes canvas text but does not transcribe or index uploaded media files — a Slack search for a word spoken in a video will return no results unless that word appears in the canvas markdown.
To preserve attribution and provenance, append a metadata block to every canvas:
# Reset SSO for contractors
Imported from Bloomfire
- Original author: Jane Doe
- Original created at: 2024-03-19 14:22 UTC
- Original updated at: 2025-01-11 09:03 UTC
- Bloomfire source ID: 18422
- Content type: Q&A
- Accepted answer author: Mark Lee
---
## Transcript / canonical answer
...That metadata block is not elegant, but it is auditable, searchable, and much better than pretending the Slack system fields represent the original history.
How Long Does This Migration Take?
| Phase | Text-Heavy (1K posts) | Media-Heavy (2K posts) |
|---|---|---|
| API extraction script development | 1–2 days | 1–2 days |
| Content extraction from Bloomfire | 2–4 hours | 1–3 days (media download) |
| Transformation (HTML → markdown, Q&A formatting) | 4–8 hours | 1–2 days |
| Slack canvas creation + file uploads | 4–8 hours | 2–5 days |
| Validation and cleanup | 1 day | 2–3 days |
| Total | 2–3 days | 7–14 days |
A text-heavy community of 1,000 posts with minimal media can be migrated in 2–3 days of engineering work. A media-heavy community of 2,000+ posts with video content adds 3–7 days for media download and re-upload alone, plus additional time for transcript extraction and Q&A formatting.
Communities over 5,000 posts with mixed media face compounding constraints: Bloomfire API rate limits (~200 RPM, undocumented), large media downloads, Slack's canvases.create rate limit (Tier 2, 20+/min), and file upload constraints. At that scale the migration can stretch to weeks, and you should evaluate whether the content should be migrated at all versus archived and replaced with a fresh knowledge base in Slack.
Who Should Not Make This Move
Some teams should not migrate Bloomfire into Slack Canvas:
- Video-first learning teams. If your Bloomfire community is a training library of screen recordings, product demos, and onboarding walkthroughs, the playback experience is the product. A transcript linked to a downloadable MP4 is not a substitute. Purpose-built LMS platforms (Docebo, TalentLMS) or dedicated video knowledge tools (Loom, Swimm) preserve inline playback, chapter navigation, and viewer progress tracking that Slack Canvas cannot replicate. Bloomfire itself may remain the better home.
- Teams that depend on accepted-answer authority. If your support or engineering team uses Q&A posts as the canonical source of truth — where the accepted answer carries compliance or operational weight — a markdown checkmark emoji is not equivalent. Stack Overflow for Teams preserves the accepted-answer data model with programmatic enforceability; Confluence preserves structured page ownership and version history. Discourse preserves threaded discussion with accepted-answer designation and voting, making it the closest structural match to Bloomfire's Q&A model.
- Communities with heavy comment-driven discussion. If the value lives in comment threads — debates, corrections, context additions — rather than the top-level post, canvas cannot preserve that interaction. Comments become static text.
- Compliance-heavy teams that need native authorship and timestamps. Slack's write APIs do not let you recreate original
created_ator reassign post ownership to the original author. This makes audit trails incomplete. - Teams on Slack free plans. Standalone canvas creation is unavailable on free-tier workspaces. This migration pattern is structurally impossible without a paid plan.
- Communities over 5,000 posts with mixed media. At that scale, evaluate archiving and rebuilding rather than migrating.
For video-first and Q&A-heavy teams, Bloomfire plus Slack integration — search in Slack, notifications in Slack, Q&A capture landing back in Bloomfire — preserves the richer data model while achieving the Slack-surface goal.
Step-by-Step Migration Process
1. Audit your Bloomfire content
Use /api/v2/posts with pagination to enumerate every post. Classify each by post_type (article, video, audio, question) and category. Count posts per type. This audit determines whether the migration is worth doing — if 80% of posts are video, reconsider your target platform.
Pilot by content type, not by department. Migrate 10 prose posts, 10 video posts, 10 audio posts, 10 Q&A posts, and 10 heavily commented posts before you estimate the full project. That is the fastest way to learn what your API path actually exposes — specifically whether transcript text is available — and what your users will consider an unacceptable loss.
2. Prove transcript availability
Make a direct API call to /api/v2/posts/{id} for one of your video posts and inspect the full JSON response. Look for any key containing transcript, transcription, captions, or full_text. Do not assume Bloomfire's UI behavior is present in API responses. If transcript text is absent from the API response, decide: request a data export from Bloomfire support, extract from rendered HTML, or re-transcribe with Whisper. This decision changes your pipeline architecture before you write a single line of transformation code.
3. Map categories to Slack channels
Create a Slack channel for each Bloomfire category using a consistent naming convention like #kb-{category-slug}. For series, decide whether each becomes a section within the channel canvas index or a separate channel. Lock this mapping before migration begins — renaming channels mid-migration breaks index canvas links.
4. Extract and transform content
For each post:
- Pull the post body from
/api/v2/posts/{id}(HTML format) - Convert HTML to markdown using a library like
markdownify(Python) orturndown(JavaScript). Strip any HTML that Canvas markdown does not support (tables may need to be validated post-conversion). - For Q&A posts (
post_type: "question"), fetch answers from/api/v2/posts/{id}/contributions, identify the object with"accepted": true, and format with the accepted-answer convention - For video/audio posts, use the transcript if available; otherwise use the description
bodyfield - Download attachments from
/api/v2/posts/{id}/attachmentsusing chunked streaming - Write a checkpoint file (JSON) mapping Bloomfire post IDs to download status and Slack canvas IDs — this enables retries without re-downloading completed items
5. Upload media to Slack
For each media file, use the three-step upload flow: files.getUploadURLExternal → POST to signed URL → files.completeUploadExternal. Share the file to the relevant channel. Store the returned permalink in your checkpoint file keyed to the Bloomfire post ID.
6. Create canvases
Call canvases.create with the transformed markdown for each post, including the provenance metadata block. Implement exponential backoff on ratelimited errors (check data ["error"], not just HTTP status). For each category channel, create or update the channel canvas with an index linking to all migrated standalone canvases. Create standalone canvases first, capture their URLs, then build the index.
7. Validate
Spot-check at least 10% of migrated canvases against original Bloomfire posts. Verify that links resolve, transcripts are complete, Q&A accepted-answer formatting is legible, and media files are downloadable. Confirm that no canvas body exceeds 1 MiB. For detailed attachment handling patterns, see How to Migrate Images, Attachments & Embeds Without Broken Links.
The Honest Trade-Off
Bloomfire to Slack Canvas works when the text is the knowledge and the media is supplementary. The transcript-as-canvas-content pattern is genuinely useful — many teams discover that the searchable transcript was always the real asset, and the video was just the capture format.
But a media-heavy Bloomfire community loses its core UX in this move. Inline playback, deep-indexed video search, accepted-answer authority, comment discussions, and engagement analytics all disappear. If those features are why your team chose Bloomfire in the first place, migrating to Slack Canvas replaces a purpose-built knowledge platform with a text document surface that happens to live inside your chat tool.
The key signals that migration makes sense: your team already navigates to Bloomfire posts via Slack links rather than Bloomfire search; your top-performing content by view count is articles rather than video; and your Q&A accepted answers are informational rather than compliance-critical. If the opposite is true for your community, the integration path — Bloomfire as system of record, surfaced through Slack — preserves the data model while achieving the consolidation goal.
That might be the right trade for consolidation and simplicity. It is not the right trade if the format is the value.
Frequently Asked Questions
- Can I migrate Bloomfire videos to Slack Canvas?
- Not as playable media. Slack Canvas is a markdown document with no embedded media player. Videos must be uploaded as Slack files and linked from the canvas body. The auto-generated transcript — if available via the Bloomfire API — becomes the actual canvas content.
- Does Bloomfire have a bulk export API for media?
- No. Media must be extracted per post using the /api/v2/posts/{id}/attachments endpoint. There is no bulk archive or ZIP export. Rate limits are approximately 200 requests per minute per token, and large files require streaming downloads to avoid memory exhaustion.
- How do Bloomfire Q&A posts translate to Slack Canvas?
- Slack Canvas has no native Q&A or accepted-answer concept. Q&A posts must be reformatted as markdown with a visual convention — such as a checkmark emoji or blockquote — to indicate the accepted answer. This is a formatting hint, not a data model, and carries no programmatic meaning.
- How long does a Bloomfire to Slack Canvas migration take?
- A text-heavy community of 1,000 posts takes roughly 2–3 days. A media-heavy community with 2,000+ video posts can take 7–14 days due to media download, re-upload, and transcript extraction. The canvases.create endpoint is rate-limited at Tier 2 (20+ requests per minute).
- What Bloomfire data is lost when migrating to Slack Canvas?
- Inline media playback, auto-generated transcripts (may not be API-accessible), video metadata, accepted-answer designation, interactive comments, likes, view counts, historical timestamps, and original author attribution on the canvas object are all lost or degraded.