Slack to Teams migration
Bring your Slack channels, channel messages, and shared files into Microsoft Teams with original timestamps and sender attribution preserved. This guide walks through the credential setup, the wizard flow, the workspace-to-team mapping rules, and the Slack and Microsoft API limits that shape what a Slack-to-Teams migration can and cannot carry across.
Before you start
MigrationFox uses Microsoft's Teams Migration API to import messages with original metadata. That API imports channel messages only — it does not accept direct messages. Plan for DMs to stay in Slack as a read-only archive, or export them separately for records retention.
What this migration carries across
- Channels — public Slack channels map to standard Teams channels under a destination team. Channel names are preserved; the wizard will warn on name collisions and on Slack characters that are not valid in a Teams channel name.
- Channel messages — message text, original timestamps, and sender attribution are imported through the Teams Migration API so the conversation timeline remains intact for audit and compliance.
- Files shared in channels — attachments uploaded into a Slack channel become files in the destination Teams channel's backing SharePoint folder. The file migration runs as a companion job queued automatically when the channel job starts.
- User attribution — Slack user IDs are resolved through your user mapping to Microsoft 365 UPNs so each imported message shows the right sender.
What does not migrate
- Direct messages (DMs) and group DMs — Microsoft's import API surface only accepts channel messages. DMs are out of scope, not a MigrationFox limitation.
- Slack apps, bots, and custom integrations — recreate these on the Teams side after migration. Workflow Builder flows and incoming webhooks do not have a 1:1 equivalent.
- Emoji reactions — the Teams Migration API does not accept reaction data, so reactions are dropped on import.
- Thread structure — Slack threads flatten into chronological order on the destination side. The messages themselves come across, but the reply tree is not preserved.
- Slack Connect (shared external channels) — external participants need to be re-invited on the Teams side as guests or federation partners. Their prior messages in the shared channel do not carry over.
Prerequisites
Workspace admin access or a Slack app with read scopes
What to grant: either connect with a Slack workspace admin account, or register a Slack app (Apps → Create New App → From scratch) with the following bot token scopes:
channels:read— list public channelschannels:history— read message history in public channelsusers:read— resolve Slack user IDs to names and emails for mapping
Install the app into your workspace and provide the bot token to MigrationFox when prompted. The app must be installed by a workspace admin for the scopes to take effect.
Why these specific scopes: they are the minimum needed to enumerate channels, read their message history, and resolve user identities. MigrationFox does not request write scopes against Slack at any point.
Azure AD app with Teamwork.Migrate.All
What to grant:
- Register an Azure AD app (or reuse the one you already use for MigrationFox).
- Under API permissions, add
Teamwork.Migrate.Allas an Application permission. - Click Grant admin consent for the destination tenant.
Why application permission: the Teams Migration API only accepts app-only tokens. Delegated tokens cannot write imported messages with original timestamps.
Slack identity → Microsoft 365 UPN
Slack user IDs (e.g. U0ABC1234) are opaque. Microsoft Teams needs a real UPN (e.g. jane@contoso.com) to attribute an imported message. Prepare a mapping list before you start so the wizard can validate it up front.
Export Slack members with their emails from your workspace directory, then match each row to the Microsoft 365 UPN that owns the same mailbox. Unmapped users cause their messages to import under a default sender rather than the intended user — fix before running, not after.
Wizard flow
The Slack-to-Teams wizard in MigrationFox runs in the same shape as any other migration job. Each step is resumable — if you close the tab mid-way, you pick up where you left off the next time you open the job.
- Add credentials. Add the Slack credential (bot token from your app, or OAuth sign-in as a workspace admin) and the Microsoft credential (OAuth with admin consent on the destination tenant). Both are validated before you can continue.
- Select source workspace and channels. MigrationFox enumerates every public channel the credential can see. Private channels require the credential to be a member; see Known limits below.
- Configure user mapping. Upload or paste your Slack-to-UPN mapping. The wizard flags unmapped active users before letting you proceed.
- Pick or create the destination team. You can target an existing team or have MigrationFox provision a new one. A new team is created in migration mode (the Teams flag that lets historical timestamps land) and flipped to active after the import completes.
- Review and start. The job runs channel-by-channel: channels are created first, messages import in order, and each channel's file attachments queue as a companion SharePoint job on the team's backing site.
Workspace-to-team mapping rules
A Slack workspace does not line up 1:1 with a Teams team. The wizard gives you two supported shapes:
- One workspace → one team. The most common choice. Every public channel becomes a standard channel in the same destination team. Simple, and keeps org-wide conversations together.
- One workspace → multiple teams. Pick subsets of channels and route each subset to a different destination team. Useful when a Slack workspace has mixed org-wide and department-scoped channels and you want the Teams structure to match the departmental split.
Channel name collisions — for example #general already existing on the target team — are flagged before run. The wizard proposes a renamed destination channel; you can accept the suggestion, edit it, or skip that channel.
Known limits
Slack free tier: 90-day visible history
On the free Slack plan, messages older than 90 days are hidden from the workspace and cannot be read back through the API, even with full admin scopes. Only messages in the visible 90-day window will migrate. Upgrading to a paid Slack plan before running the migration unlocks the full history; this is the single most common cause of “most of our messages didn't come over.”
- Private channels migrate only when the credential is a member. Private Slack channels are invisible to non-members through the API — there is no admin override. Add the migration user to each private channel you want moved, or skip them.
- Imported messages render as “imported” in Teams, not as native posts. This is the Teams Migration API's documented behavior and applies to every tool that uses the API. The full message text, sender, and timestamp are intact; the UI just tags the message so users can tell it was backfilled.
- Slack reactions are dropped. The Microsoft import API does not accept reaction payloads. Reactions are not preserved on either side after the migration runs.
- Threads flatten. Replies come across in chronological order with the parent message, not nested under it.
- Slack Connect external channels cannot be migrated. External participants must be invited as guests on the Teams side.
- Slack apps and custom integrations do not carry over. Rebuild the destination flows using Power Automate, Teams apps, or incoming webhooks.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Channel enumeration returns far fewer channels than the workspace has | The Slack credential lacks channels:read, or private channels exist that the user is not a member of. |
Verify the Slack app has channels:read installed; add the migration user to the private channels that matter; rerun the scan. |
| Messages older than roughly 90 days are missing | Slack free-tier history retention; older messages are not returned by the API. | Upgrade the Slack workspace before running the migration, or accept that only the visible 90-day window will migrate. |
| Migration fails with “Teamwork.Migrate.All not granted” or HTTP 403 on channel creation | The Azure AD app has the permission but admin consent was never clicked; or the consent was granted in the wrong tenant. | Go to Azure AD → your app → API permissions → Grant admin consent. Confirm you are in the destination tenant. |
| All imported messages show the same sender (a default user) instead of the original Slack author | The user mapping had Slack IDs or emails that did not match any UPN in the destination tenant. | Export the mapping from the job page, fix the unmatched rows, and re-run. Mapping changes only apply on fresh runs — already-imported messages keep the attribution they landed with. |
| Channel imports but files never appear in the Teams channel's Files tab | The companion SharePoint file job hasn't finished yet, or the destination site storage quota is full. | Check the file job's status on the job detail page; companion jobs run after the message import, so expect a delay on large channels. Resolve any SharePoint quota error and the job resumes. |
| A channel is reported as “name invalid” by the wizard | The Slack channel name contains characters Teams rejects (e.g. #, certain punctuation) or collides with an existing team channel. |
Accept the suggested rename in the wizard or edit it. Teams channel naming rules are stricter than Slack's — the wizard tells you exactly what it will use. |
| Emoji reactions and threaded reply structure are missing after migration | Expected — the Microsoft import API does not accept reactions and flattens threads. | Not fixable at the API layer. Treat this as a documented limit, communicate to end users before cutover. |
Related
- Teams to Teams migration — cross-tenant Teams channel migration with the same Teams Migration API backbone
- SharePoint Site Migration — what happens to channel files on the backing site