Skip to content

Instantly share code, notes, and snippets.

@sshh12
Last active March 8, 2026 05:35
Show Gist options
  • Select an option

  • Save sshh12/4cca8d6698be3c80e9232b68586b7924 to your computer and use it in GitHub Desktop.

Select an option

Save sshh12/4cca8d6698be3c80e9232b68586b7924 to your computer and use it in GitHub Desktop.
A reverse-engineered system design of Slack's web application, built from live network traffic analysis of the authenticated Enterprise Grid experience. 200+ API calls captured across boot, search, messaging, reactions, and navigation. Every backend service named.

Slack System Design: A Grounded Teardown

A reverse-engineered system design of Slack's web application, built from live network traffic analysis of the authenticated Enterprise Grid experience. 200+ API calls captured across boot, search, messaging, reactions, and navigation. Every backend service named.

Architecture Overview

┌─────────────────────────────────────────────────────────────────────────────┐
│  BROWSER (Gantry v2 SPA)                                                    │
│                                                                             │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌───────────────┐   │
│  │  React UI    │  │  Quip/Canvas │  │  Real-time   │  │  Sonic        │   │
│  │  (Gantry v2) │  │  Engine      │  │  (WebSocket) │  │  Boot Client  │   │
│  └──┬───────────┘  └──┬───────────┘  └──────┬───────┘  └───────┬───────┘   │
│     │                  │                     │                  │           │
└─────┼──────────────────┼─────────────────────┼──────────────────┼───────────┘
      │                  │                     │                  │
      │                  │                     │                  │
┌─────┼──────────────────┼─────────────────────┼──────────────────┼───────────┐
│  EDGE                  │                     │                  │           │
│     ▼                  ▼                     │                  │           │
│  ┌──────────────────────────────────┐        │                  │           │
│  │    Envoy Edge Proxy              │        │                  │           │
│  │    (pdx / iad regions)           │        │                  │           │
│  └──┬──────────────┬────────────────┘        │                  │           │
└─────┼──────────────┼────────────────────────┼──────────────────┼───────────┘
      │              │                        │                  │
┌─────┼──────────────┼────────────────────────┼──────────────────┼───────────┐
│  BACKEND SERVICES  │                        │                  │           │
│     │              │                        │                  │           │
│     ▼              ▼                        ▼                  ▼           │
│  ┌─────────┐  ┌──────────┐  ┌──────────────────┐  ┌───────────────────┐   │
│  │Workspace│  │ Flannel  │  │  ListenWeb       │  │ HHVM Callbacks    │   │
│  │  API    │  │ (Edge    │  │  (WebSocket via  │  │ (client.userBoot  │   │
│  │ (HHVM) │  │  Cache)  │  │   Quip/onquip)   │  │  deferred data)   │   │
│  └────┬────┘  └────┬─────┘  └──────────────────┘  └───────────────────┘   │
│       │            │                                                       │
│       │       ┌────▼──────────────────────────────┐                       │
│       │       │  Loom (Distributed Data Index)    │                       │
│       │       │  ├─ Channel model                 │                       │
│       │       │  ├─ ChannelMembershipByUserId     │                       │
│       │       │  ├─ ESCMembershipByUserId         │                       │
│       │       │  └─ (loom-index / loom-router)    │                       │
│       │       └───────────────────────────────────┘                       │
│       │                                                                    │
│  ┌────▼────────────────────────────────────────────────────────────────┐   │
│  │  Quip Backend (Canvas/Docs)                                        │   │
│  │  ├─ slack-prod.onquip.com (API + real-time collab)                 │   │
│  │  ├─ listenweb3.slack-prod.onquip.com (WebSocket listener)         │   │
│  │  └─ api.slack-prod.onquip.com                                     │   │
│  └────────────────────────────────────────────────────────────────────┘   │
└───────────────────────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────────────────────┐
│  CDN / STATIC                                                             │
│  ┌──────────────────────┐  ┌──────────────────────────────────────────┐   │
│  │  a.slack-edge.com    │  │  canvas_blob (a.slack-edge.com)          │   │
│  │  (JS/CSS/fonts)      │  │  (Quip CRDT engine chunks)              │   │
│  └──────────────────────┘  └──────────────────────────────────────────┘   │
└───────────────────────────────────────────────────────────────────────────┘

The Two API Tiers

Slack runs two distinct API services with different routing, backends, and data models.

1. Workspace API (Legacy / Primary)

Endpoint: POST https://<workspace>.enterprise.slack.com/api/<method>

This is the traditional Slack API -- every method is a separate POST endpoint using multipart form-data with a token field. All 40+ API calls during boot go through this path.

Every request includes query params that reveal the client framework:

?_x_id=912a4723-1772325609.377     # request correlation ID
&_x_csid=8GdRRm8e6Es               # client session ID
&slack_route=E03C83N442J:E03C83N442J  # enterprise:enterprise routing
&_x_version_ts=1772314064           # JS bundle version (Unix timestamp)
&_x_frontend_build_type=current     # build type
&_x_desktop_ia=4                    # desktop info architecture version
&_x_gantry=true                     # Gantry v2 framework flag
&fp=95                              # feature parity version
&_x_num_retries=0                   # retry count

The _x_sonic=true form field in boot requests signals the client supports the Sonic boot protocol (Slack's optimized cold-start system).

Response headers reveal the backend:

  • x-server: slack-www-hhvm-callbacks-iad-wfwr1kv7kl7i -- runs on HHVM (HipHop Virtual Machine, Facebook's PHP runtime)
  • x-slack-backend: r -- backend type identifier
  • x-backend: callbacks_normal or api_normal -- request class routing
  • via: 1.1 slack-prod.tinyspeck.com, envoy-www-iad-* -- Tinyspeck origin + Envoy proxy in IAD (us-east)
  • x-edge-backend: envoy-www -- edge layer routes to envoy-www

2. Edge API (Flannel)

Endpoint: POST https://edgeapi.slack.com/cache/<enterprise_id>/<resource>/<action>

This is Slack's edge caching layer called Flannel. It uses JSON request bodies (not form-data) and is backed by a distributed index called Loom.

Resources and actions observed:

/cache/E03C83N442J/users/info      # batch-fetch user profiles by ID
/cache/E03C83N442J/users/list      # list users
/cache/E03C83N442J/users/counts    # user count stats
/cache/E03C83N442J/channels/info   # batch-fetch channel metadata
/cache/E03C83N442J/channels/membership  # channel membership check
/cache/E03C83N442J/permissions/info    # permission checks (5 calls!)
/cache/E03C83N442J/huddles/info    # huddle/call state
/cache/E03C83N442J/emojis/info     # custom emoji data

The channels/info response headers expose the full Loom topology:

x-backend: flannel-prod-pdx-6d7b8f8579-7mh6b,
           loom-index-prod-pdx-108#slack.loom.model.Channel,
           loom-index-prod-pdx-108#slack.loom.model.ChannelMembershipByUserId,
           loom-index-prod-pdx-108#slack.loom.model.ESCMembershipByUserId,
           loom-router-prod-pdx-65755785d8-4jg58#slack.loom.model.Channel

This reveals Loom's architecture:

  • loom-router pods route queries to the correct index shard
  • loom-index pods hold the actual data, partitioned by model type
  • Models include Channel, ChannelMembershipByUserId, ESCMembershipByUserId (Enterprise Shared Channels)
  • Each model instance is on a numbered pod (e.g., loom-index-prod-pdx-108)

Key difference: The Edge API uses conditional fetching -- the client sends updated_ids with timestamps for each entity it already has cached, and the server only returns entities that have changed since those timestamps. This is how Slack avoids re-fetching the entire user/channel list on every load.

The Boot Sequence

Slack's boot is a carefully staged pipeline. Here's the actual order from the network trace:

Phase 1: Pre-boot (before SPA renders)

1. client.shouldReload      -- check if client version is stale
2. features.access.policies.list  -- org-level access policies
3. experiments.getByUser    -- A/B test assignments (36KB! ~100 experiments)
4. api.features             -- feature flags (~60 flags)
5. enterprise.prefs.get     -- enterprise-level preferences
6. conversations.view       -- current channel metadata
7. client.userBoot          -- the big one: user data, DND state, channel list,
                               channel priority scores, team membership

Phase 2: Parallel data fetch (SPA rendering starts)

8-12.  users.prefs.get, drafts.list, client.extras,
       search.autocomplete.topEmojis, workflows.triggers.*
13-18. Edge API: users/info, permissions/info, huddles/info,
       emojis/info (all using conditional timestamps)
19.    users.priority.list -- ranked contacts for @-mention autocomplete

Phase 3: Channel-specific data

20-25. conversations.history, bookmarks.list, saved.list,
       conversations.listPrefs, conversations.bulkReacjiTriggers,
       users.channelSections.list

Phase 4: Feature-specific lazy loads

26+.   megaphone.notifications.list, aiApps.list,
       slackAi.permissions.getForUser, calendar.*, sfdc.*,
       help.issues.ticketStats, sharedInvites.canGetLink,
       canvas/collab/controller-init

Channel Priority Scoring

The client.userBoot response includes a channels_priority map that assigns every channel a relevance score between 0 and 1:

channel_id → score (0.0 to 1.0)

These scores determine sidebar ordering. Scores approaching 1.0 are channels the user interacts with most frequently; scores near 0 are rarely-visited channels. The scoring clearly uses interaction frequency, recency, and possibly message volume. DMs with close collaborators score as high as the most-used channels.

Experimentation System

The experiments.getByUser response is one of the largest boot payloads (~36KB). Each experiment includes:

{
  "experiment_name": {
    "experiment_id": "10549498611876",
    "type": "team" | "user",
    "group": "on" | "off" | "treatment" | "control" | "aa_treatment_c",
    "trigger": "hash_team" | "hash_user" | "finished" | "launch_team" | "launch_user",
    "schedule_ts": 1772217597,
    "log_exposures": true | false,
    "exposure_id": "<team_or_user_id>"
  }
}

Interesting experiments visible in the response:

  • agent_sunroof -- AI agent features
  • slackbot_ai_next, slackbot_ai_client_context -- Slack AI iterations
  • huddles_dual_udp_support, huddles_agc_browser_default -- audio/video calling improvements
  • splitview_child_windows, split_view_nux -- multi-panel UI
  • optimize_sidebar_latests -- sidebar performance experiment
  • channel_store_stable_updates, members_store_stable_updates -- data layer experiments
  • omniswitcher_memoization -- Cmd+K switcher performance
  • svg_rendering_svgr -- SVG rendering approach test
  • lists_grid_todos, lists_records_list_5k -- Slack Lists scaling
  • new_joiner_workspace_switcher_prompt -- onboarding flow

The trigger field reveals the randomization strategy:

  • hash_team / hash_user -- deterministic hash-based assignment (stable across sessions)
  • launch_team / launch_user -- explicit rollout (not randomized)
  • finished -- experiment completed, now at 100%

Canvas / Quip Integration

Slack Canvas runs on Quip's infrastructure (Salesforce acquisition). The controller-init endpoint reveals:

  • Real-time collab server: listenweb3.slack-prod.onquip.com (WebSocket endpoint at /-/listen/3 on port 443)
  • API server: api.slack-prod.onquip.com
  • CDN: a.slack-edge.com/canvas_blob/ for CRDT engine chunks
  • Quip team ID: T5J4Q04QG -- Slack's internal Quip org
  • OAuth token: Canvas has its own OAuth token with a separate expiry (12h TTL)
  • Build version: quipbuild_2026_02_26T21_00_d87d87e -- daily builds with git hash

The controller-init response includes a serialized protobuf (init_options) containing the full CRDT document state and user context, plus a massive js_initializer config with Quip-specific settings (emoji CDN hashes, Salesforce integration config, folder depth limits, max list sizes).

Canvas chunks are loaded separately from the main Slack JS bundle -- 6 blobs with content-hash-based URLs (canvas_blob/<hash>-module-loader, canvas_blob/<hash>-ancillary, etc.).

Gantry v2 (Client Framework)

Slack's web client framework is called Gantry v2. The JS bundle structure:

gantry-v2-vendors.js           -- third-party deps (React, etc.)
gantry-v2-vendors-client.js    -- client-specific vendor deps
gantry-v2-shared.js            -- shared framework code
client-boot.js                 -- boot orchestration
  ├─ async-client-boot-apis.js     -- API client layer
  ├─ async-client-boot-data.js     -- data stores
  ├─ async-client-boot-render.js   -- initial render
  ├─ async-client-boot-deferred.js -- deferred initialization
  ├─ async-gantry-v2-shared-boot-async.js
  ├─ async-gantry-v2-default-boot-shared.js
  └─ async-gantry-v2-vendors-async*.js

Route-specific lazy chunks:
  ├─ async-client-channel-list-view.js  -- sidebar
  └─ async-client-channel-view.js       -- message pane

All JS/CSS is served from a.slack-edge.com with content-hash filenames and a cacheKey=gantry-<version_ts> query param for cache busting. The version timestamp (1772314064) changes with each deployment.

Identity & Auth

Token Types

Token Format Purpose
xoxc-* Cookie-bound client token Primary API auth -- sent in every request body as token field
xoxd-* Cookie d Session token -- HttpOnly, stored as cookie
b cookie .cd5ce331... Browser identifier (persistent)
x cookie <hash>.<ts> Short-lived session binding (15min TTL, refreshed)
d-s cookie Unix timestamp Session start time

The xoxc-* token is notable -- it's sent in the request body (not as a header or cookie), which means it can be rotated per-request without cookie management. The xoxd-* token in the d cookie is the persistent auth credential.

Enterprise Grid Routing

The URL pattern <workspace>.enterprise.slack.com and the slack_route=E03C83N442J:E03C83N442J param reveal Enterprise Grid routing. The enterprise ID appears in both the URL path and the Edge API path (/cache/E03C83N442J/), enabling multi-org routing.

Real-Time Messaging

The Canvas system connects via WebSocket to listenweb3.slack-prod.onquip.com/-/listen/3 for real-time document collaboration.

For message real-time updates, Slack uses a combination of:

  • Long-lived WebSocket connections (for push notifications and message delivery)
  • The client.counts API for periodic unread count reconciliation
  • The conversations.history API with oldest timestamp for loading message history

Push notifications are managed via notifications.netflix.com/push-style long-polling (Slack's equivalent appears to be through the WebSocket or the megaphone.notifications.list API).

Named Internal Systems

System Purpose Evidence
Gantry v2 Web client SPA framework _x_gantry=true param, gantry-v2-* JS bundles
Sonic Optimized cold-start boot protocol _x_sonic=true form field in boot requests
Flannel Edge caching/proxy layer x-backend: flannel-prod-pdx-*, x-edge-backend: flannel
Loom Distributed data index (channels, users, memberships) loom-index-prod-pdx-*, loom-router-prod-pdx-* in x-backend
Quip Canvas/document collaboration engine slack-prod.onquip.com, x-quip-tracer-id header
HHVM PHP runtime for Workspace API x-server: slack-www-hhvm-*
Tinyspeck Slack's original company name, still in infra via: 1.1 slack-prod.tinyspeck.com
Envoy Edge proxy (multi-region) envoy-www-iad-*, envoy-edge-pdx-*
Megaphone In-app notification/announcement system megaphone.notifications.list API
ListenWeb WebSocket real-time server (for Canvas) listenweb3.slack-prod.onquip.com

What You'd Need to Build This

Core Backend Services

  1. Messaging Service -- conversation history, threading, reactions, message formatting (Block Kit)
  2. Channel Service -- channel CRUD, membership, sections/folders, channel priority scoring
  3. User Service -- profiles, presence/DND, status, preferences, custom status
  4. Auth Service -- token management (xoxc/xoxd), enterprise routing, session binding
  5. Search Service -- full-text search, autocomplete, emoji ranking
  6. Notification Service -- unread counts, mentions, @-channel alerts, megaphone announcements
  7. Drafts Service -- per-channel draft persistence
  8. Bookmarks/Saved Service -- pinned items, saved messages
  9. Experimentation Service -- A/B test assignment, hash-based bucketing, exposure logging

Real-Time Infrastructure

  1. WebSocket Gateway -- persistent connections, message fan-out, presence updates
  2. Edge Cache (Flannel) -- conditional fetch with timestamps, sharded by entity type
  3. Distributed Index (Loom) -- channel/user/membership data, partitioned by model, routed by entity

Collaboration

  1. Document Engine -- CRDT-based collaborative editing (Canvas/Quip)
  2. Huddles Service -- audio/video calling, participant tracking

Platform

  1. Workflow Engine -- triggers, functions, automation
  2. App Platform -- bot users, slash commands, interactive messages, app profiles
  3. Feature Flag Service -- feature gates + experiment assignments in a single boot payload

Infrastructure

  • Multi-region Envoy -- IAD (us-east) for API origin, PDX (us-west) for edge
  • HHVM -- PHP runtime for legacy API layer
  • Chunk-based JS delivery -- 13+ async chunks with content-hash filenames
  • Cookie-bound token auth -- xoxc token in body, xoxd in HttpOnly cookie, short-lived x cookie for session binding

Search Architecture

Search is one of Slack's most complex subsystems. A single search fires 7 parallel API calls across two API tiers:

Typeahead / Quick Search

Workspace API:
  search.inline          -- quick 3-result preview in current channel
  search.autocomplete    -- suggestion dropdown as you type
  search.autocomplete.files  -- file-specific autocomplete

Edge API (Flannel):
  users/search           -- user name matching
  channels/search        -- channel name matching

Full Search Results (fires when you hit Enter)

search.modules.messages   -- full-text message search (9,551 results, paginated 20/page)
search.modules.files      -- file search (called twice: different file types)
search.modules.channels   -- channel name/topic search
search.modules.people     -- people search
search.modules.dms        -- DM-specific search
search.save               -- persists the query to search history
search.precache           -- pre-warms results for likely next queries

The search.modules.messages request reveals the search engine internals:

  • spell_correction: FUZZY_MATCH -- fuzzy matching is built in
  • sort: score / sort_dir: desc -- relevance scoring, not chronological by default
  • facets_result_count: 5 -- returns top 5 filter facets (from, in, date)
  • query_refinement_suggestions_version: 1 -- the server can suggest query rewrites
  • search_context: desktop_messages_tab -- search knows which UI context triggered it
  • recent_channels -- passes recent channel IDs for context-aware ranking
  • request_context -- includes active_cid, recent_filter_in, recent_filter_from for personalization
  • extract_len: 200 -- server-side snippet extraction with highlight markers (\ue000..\ue001)
  • Highlighting uses Unicode private-use characters -- \ue000 marks highlight start, \ue001 marks end. The client renders these as bold/highlight.

Search results include client_highlight styling in Block Kit -- the server returns the original Block Kit structure but with an extra "client_highlight": true style on matched text spans.

Error Reporting

The slack.com/beacon/error endpoint receives client-side error reports. Multiple error beacons fired during search, suggesting error boundary monitoring is aggressive.

Message Send & Reaction API Contracts

Sending a Message (chat.postMessage)

Messages are sent via the Workspace API as multipart form-data. The key fields:

channel:      C09KB2J6QNN           # channel ID
ts:           1772325870.xxxxx2     # client-generated timestamp (optimistic)
type:         message
client_msg_id: a789f1c4-...         # UUID for dedup
_x_reason:    webapp_message_send
_x_mode:      online
blocks:       [{"type":"rich_text","elements":[{"type":"rich_text_section",
               "elements":[{"type":"text","text":"..."}]}]}]

The response returns the server-assigned timestamp (ts: "1772325870.309319") which becomes the message's permanent ID. The client_msg_id UUID enables deduplication -- if the request is retried, the server recognizes it as the same message.

Messages use Block Kit format -- even plain text is wrapped in rich_text > rich_text_section > text blocks. This structure supports inline formatting (bold, italic, code), mentions, links, and emoji as first-class block elements.

Adding a Reaction (reactions.add)

channel:      C09KB2J6QNN
timestamp:    1772325870.309319     # message ts (the message ID)
name:         eyes                  # emoji name (no colons)
_x_reason:    changeReactionFromUserAction

Reactions reference messages by channel + timestamp -- timestamps are message IDs in Slack. The response is simply {"ok":true}.

Client-Side Tracking (clog/track/)

Every user interaction (clicking a reaction, opening a channel) fires a clog/track/ POST for analytics. This is Slack's client-side event logging system, separate from the _x_reason field on API calls (which is server-side request attribution).

Non-Obvious Implementation Details

The Edge API uses conditional timestamp fetching. Every request to edgeapi.slack.com includes updated_ids -- a map of entity IDs to the last-known update timestamp. The server only returns entities that changed after those timestamps. This is how Slack avoids fetching 100K+ users on every page load in large orgs.

Permissions are checked 5 times during boot. The permissions/info endpoint is called 5 separate times with different permission sets. This suggests permission checks are decoupled from entity fetching and evaluated lazily per-feature.

Slack still runs on HHVM. The x-server header reveals hostnames like slack-www-hhvm-api-iad-* and slack-www-hhvm-callbacks-iad-*. HHVM (Facebook's PHP runtime) is the execution engine for the Workspace API, with separate pools for "api" (synchronous requests) and "callbacks" (boot data, potentially heavier queries).

Tinyspeck is still in production. The via header includes slack-prod.tinyspeck.com -- Tinyspeck was Slack's original company name (from the game Glitch). It persists in production routing infrastructure.

Canvas is essentially a separate app. It has its own OAuth token, its own WebSocket connection, its own CDN path (canvas_blob/), its own build versioning, and its own real-time infrastructure on Quip's domain. It's bolted onto Slack's UI but architecturally independent.

The boot payload carries ~100 A/B experiments. At 36KB, the experiment assignment is one of the largest single payloads during boot. Every experiment includes its randomization strategy, schedule timestamp, and exposure logging config.

Channel sections are a first-class concept. users.channelSections.list reveals that the sidebar's channel groupings (favorites, DMs, channels) are a server-side data model, not just client-side UI state.

The client proactively detects ad blockers. The ublockworkaround.history API endpoint is explicitly named after uBlock Origin -- Slack detects when ad blockers interfere with their requests and has a workaround path.

Salesforce integration is deeply embedded. The sfdc.integration.listOrgs API and extensive Salesforce config in the Canvas js_initializer (Steam types, STMF types, vPod hosts) show deep platform integration beyond just the Slack-Salesforce connector.

Feature parity tracking. The fp=95 query param on every request likely tracks "feature parity" -- how close the web client is to the desktop client's feature set. This number presumably increments as web catches up.

DOMContentLoaded in 177ms. Slack achieves extremely fast DCL because the initial HTML is a minimal shell -- all real rendering happens after the JS boots and the client.userBoot data arrives. First contentful paint takes 2,160ms (the real "loaded" moment).

Two deployment regions are visible. API origin servers are in iad (us-east / Virginia) while edge proxies run in pdx (us-west / Portland). The via header shows requests traverse both: edge in PDX, origin in IAD.

All API tokens are sent in request bodies, not headers. Unlike most APIs that use Authorization headers, Slack sends the xoxc-* token as a form field or JSON body property. This is a legacy design from when the API was primarily used by web forms.

Timestamps are message IDs. Slack's ts field (e.g., 1772325870.309319) serves as both a timestamp and a unique message identifier. This is a core design choice -- messages are ordered and addressed by their creation time, and the decimal portion ensures uniqueness within a second.

Client generates an optimistic timestamp. When sending a message, the client sends its own ts estimate, but the server returns the authoritative one. The client_msg_id UUID provides deduplication if the network retries.

Every API call includes a reason tag. The _x_reason field on every request (webapp_message_send, changeReactionFromUserAction, deferred-data) gives the backend attribution for why the call was made. This enables per-feature performance tracking and cost attribution.

Block Kit is the universal message format. Even a plain text message like "hello" is encoded as rich_text > rich_text_section > text blocks. This isn't overhead -- it's what enables inline formatting, mentions, emoji, and links to be first-class elements in the message structure rather than parsed from markdown.

Quick reactions are server-personalized. The hover toolbar shows "React with white_check_mark / eyes / raised_hands" -- these are the user's most-used reactions, fetched at boot via search.autocomplete.topEmojis.

B3 distributed tracing appears selectively. Most API calls don't include trace headers, but some Edge API calls include _x_b3_traceid, _x_b3_spanid, and _x_b3_sampled as query params. This suggests sampling-based tracing rather than universal instrumentation.

The _x_mode field reveals offline capability. Every write API call includes _x_mode: online. This implies Slack's client has an offline mode where operations are queued and replayed -- the mode field tells the server whether this is a live or replayed request.

Link unfurling is entirely server-side. When you send a message containing a URL, chat.postMessage includes an unfurl: [] field (empty -- the client doesn't prefetch). The unfurl card (title, description, thumbnail) appears asynchronously as the server fetches the URL metadata and pushes an update via the real-time connection. For GitHub links, the GitHub Slack app handles the unfurl via Slack's app unfurl API rather than generic OGP scraping.

Search precaching is real. After displaying results, the client fires search.precache -- likely pre-warming the next page or related queries. This explains why paginating search results feels instant.

Cmd+K search runs on the Edge API. The quick-switcher (Cmd+K) uses edgeapi.slack.com/cache/.../users/search and channels/search rather than the Workspace API. This means user/channel search is backed by Loom (the distributed index), not the main search backend. Faster, but different indexing.

Every search has a session ID. The search_session_id UUID persists across pagination and refinements within a single search session. This enables the backend to track search funnels: did the user find what they wanted, or did they refine the query?

users.prefs.set fires on navigation. When you switch channels, Slack saves the current channel to your preferences via users.prefs.set. This is how it knows which channel to reopen when you return.

Error reporting goes to a separate domain. Client errors are reported to slack.com/beacon/error -- not the workspace domain or edge API. This separate path ensures error reporting works even when the main API is down.

Reacji triggers are checked on every channel load. conversations.bulkReacjiTriggers fires whenever you open a channel -- any emoji reaction in that channel could trigger a workflow automation. The client needs to know which emoji are "live" so it can show trigger indicators.

8 thumbnail sizes are pre-generated per uploaded image. Search results revealed sizes at 64, 80, 160, 360, 480, 720, 800, 960, and 1024px. These are generated server-side on upload, not on-the-fly -- the URLs use files-tmb/ (thumbnails) vs files-pri/ (originals).

client.shouldReload is the deployment mechanism. The very first boot call asks the server "is my JS version stale?" This is how Slack ships without forcing hard refreshes -- the server compares _x_version_ts against the current deployment and tells the client to reload if needed.

All unfurled images are proxied through slack-imgs.com. External image URLs in link previews are rewritten to slack-imgs.com/?c=1&o1=wi32.he32.si&url=<encoded_url> -- Slack never loads images directly from third-party origins. This prevents IP leakage, enables resizing, and provides a cache layer.

OAuth scope headers appear on every response. Both x-accepted-oauth-scopes (what the endpoint requires) and x-oauth-scopes (what the token has) are returned on every API call. The client can detect permission mismatches without making a separate authz call.

Client session ID rotates per view. The _x_csid parameter changes when you navigate between channels or views -- each "session" gets its own correlation ID separate from the boot session. This enables per-view performance tracking and error attribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment