> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getasym.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Slack template

> A real Slack-style backend, frontend, and MCP server — RPC Web API, the {ok} envelope, Slack-format ids, and byte-identical events.

The Slack template (`apps/slack-clone`, name `slack`) is the reference clone: a
NestJS backend on Postgres, an optional React frontend, and an MCP server. Its
data plane is the **real Slack Web API** — RPC method names (`chat.postMessage`),
args in the request body, the `{ "ok": true, … }` envelope, Slack-format object
ids (`T…/U…/C…/D…/G…/A…`), per-method rate-limit tiers, and byte-identical Events
API webhooks. Auth works two ways: a JWT for the web UI, and Slack-style opaque
**bearer tokens** (`xoxb-…` / `xoxp-…`) for programmatic access. It's faithful
enough at the **wire-format level** that a client built for `@slack/web-api` can
drive it.

```bash theme={null}
asymmetric spin slack
```

Spinning a clone auto-provisions a default app and prints a **bot token** and a
**user token** you can use immediately — see [Apps and tokens](#apps-and-tokens).

## What's inside

| Piece                  | Notes                                                                                                                                      |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `backend/`             | NestJS Slack Web API (RPC) + Socket.io realtime, Postgres via `pg`. Global prefix `/api`, health at `/api/health`, Swagger at `/api/docs`. |
| `frontend/`            | React + Vite UI. Runs only in `full` mode.                                                                                                 |
| `mcp-server/`          | MCP server exposing the eight reference Slack MCP tools (+ labeled extensions).                                                            |
| `supabase/migrations/` | `001_initial_schema.sql` + `002_apps_and_tokens.sql` — the schema (incl. `gen_slack_id()`), run on spin and replayed on reset.             |
| `seeds/`               | `acme-corp.sql` — the bundled deterministic fixture.                                                                                       |

## Modes

| Mode            | Services               | Use it for                                     |
| --------------- | ---------------------- | ---------------------------------------------- |
| `api` (default) | `backend`              | Agents that talk to the Web API or MCP server. |
| `full`          | `backend` + `frontend` | When you also want the UI.                     |

```bash theme={null}
asymmetric spin slack --mode full
```

## Seeding

```bash theme={null}
asymmetric spin slack --seed acme-corp     # deterministic fixture on create
asymmetric seed slack-a1b2 --ai            # realistic data via the Web API
```

AI seeding generates `users`, `channels`, and `messages` (the template's declared
entities) and creates them through real signup / `conversations.create` /
`chat.postMessage` calls. See [Seeding](/concepts/seeding).

## Apps and tokens

Real Slack apps authenticate with bearer tokens (`xoxb-` for bots, `xoxp-` for
users) and are configured by an app manifest. The clone mirrors that: it has a
first-class **app** entity with a stored manifest, opaque bearer tokens, and
**manifest scopes that are enforced** on every app-token call.

### Tokens you get for free

Every `spin slack` auto-provisions a default workspace, an admin user, and a
default app, then prints a bot token and a user token:

```
  ✓ slack-a1b2  (api)  http://127.0.0.1:3001/api  ready in 38.2s
    db clone_slack_a1b2 · env -
    bot token   xoxb-…
    user token  xoxp-…
```

Use either as a bearer token against the Web API — no signup/login step. The
identity check is `auth.test` (the real Slack method), POSTed with the token:

```bash theme={null}
curl -s -X POST http://127.0.0.1:3001/api/auth.test \
  -H "authorization: Bearer xoxb-…"
# → { "ok": true, "user_id": "U…", "team_id": "T…", "user": "…", … }
```

A **bot token** acts as the app's bot user (messages it posts are attributed to
the bot, `is_bot: true`); a **user token** acts as the admin user. This is the
fastest way to point an agent at a clone — see
[Connect your agent](/guides/connect-agent#option-a-the-rest-api).

### Get the tokens again later

Tokens are stored locally so you can reprint them any time:

```bash theme={null}
asymmetric tokens slack-a1b2            # print the bot + user tokens
asymmetric tokens slack-a1b2 --json     # machine-readable
asymmetric tokens slack-a1b2 --reprovision   # rotate (old tokens stop working)
```

See the [`tokens` reference](/cli/tokens).

### Create your own app

Post a Slack-shaped manifest to create additional apps (each gets its own bot
user and bot token). App management is a clone-operation, so it uses a plain REST
shape, not the `{ ok }` envelope:

```bash theme={null}
curl -s -X POST http://127.0.0.1:3001/api/apps \
  -H "authorization: Bearer xoxp-…" \
  -H 'content-type: application/json' \
  -d '{"manifest":{"display_information":{"name":"My App"},"oauth_config":{"scopes":{"bot":["chat:write","channels:read"]}}}}'
```

<Note>
  App-token **scopes are enforced.** Each Web API method declares the Slack scope
  it needs (`chat:write`, `reactions:write`, `channels:history`, `search:read`, …);
  a token missing it gets `403 { "ok": false, "error": "missing_scope" }`. JWT /
  web-UI humans are exempt. OAuth install and `auth.revoke` are not implemented yet.
  Clones bind to localhost by default, so a token is not reachable off your machine
  unless you `--expose`.
</Note>

## API shape — read this before pointing an agent at it

The data plane **is** Slack's RPC Web API, so it matches at the wire-format level,
not just the capability level:

* **RPC method routes** — `POST /api/chat.postMessage`, `conversations.*`,
  `reactions.*`, `users.*`, `search.messages`, `auth.test` — args in the JSON body
  (`channel`, `ts`, `timestamp`, `users`, `name`, …), exactly how `@slack/web-api`
  calls them.
* **The `{ ok }` envelope** — success is `{ "ok": true, … }`; failures are
  **HTTP 200** `{ "ok": false, "error": "<code>" }`, with cursor pagination under
  `response_metadata.next_cursor`.
* **Slack-format ids** — every object id is minted in Slack's grammar at the DB
  layer (`T…` team, `U…` user, `C…` channel, `D…` IM, `G…` mpim, `A…` app). No
  UUIDs leak into payloads.
* **Rate-limit tiers** — each method enforces its Slack tier; a breach returns
  `HTTP 429` + `Retry-After` + `{ "ok": false, "error": "ratelimited" }` (see
  below).
* **Byte-identical events** — outgoing webhooks deliver the full Slack Events API
  `event_callback` envelope with inner objects matching Slack field-for-field
  (`message`, `reaction_added/removed`, `channel_created`, `channel_archive`,
  `member_joined/left_channel`, `user_change`, `presence_change`).

A quick tour against a running clone:

```bash theme={null}
B=http://127.0.0.1:3001/api
A="authorization: Bearer xoxb-…"

# Post a message (channel id is a Slack-format C…)
curl -s -X POST $B/chat.postMessage -H "$A" -H 'content-type: application/json' \
  -d '{"channel":"C…","text":"hello from an agent"}'

# List conversations, then read history
curl -s -X POST $B/conversations.list    -H "$A" -d '{"types":"public_channel"}'
curl -s -X POST $B/conversations.history  -H "$A" -d '{"channel":"C…","limit":20}'

# React to a message by channel + ts (Slack's keying, not a UUID)
curl -s -X POST $B/reactions.add -H "$A" \
  -d '{"channel":"C…","timestamp":"1718900000.000100","name":"thumbsup"}'

# Full-text search across visible channels
curl -s -X POST $B/search.messages -H "$A" -d '{"query":"hello"}'
```

### Implemented method surface

The data plane implements **27 Slack methods** across messaging, conversations,
reactions, users, search, and auth:

| Family          | Methods                                                                                                                                             |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `chat`          | `postMessage`, `update`, `delete`                                                                                                                   |
| `conversations` | `list`, `create`, `info`, `history`, `replies`, `members`, `join`, `leave`, `archive`, `invite`, `kick`, `rename`, `setTopic`, `setPurpose`, `open` |
| `reactions`     | `add`, `remove`                                                                                                                                     |
| `users`         | `list`, `info`, `profile.get`, `profile.set`, `setPresence`                                                                                         |
| `search`        | `messages`                                                                                                                                          |
| `auth`          | `test`                                                                                                                                              |

Whole Slack families outside this core (`files.*`, `pins.*`, `bookmarks.*`,
`dnd.*`, `views.*`, `usergroups.*`, `admin.*`, …) are unimplemented by design —
this is an MVP+Beta vertical slice, not the full \~130-method Web API.

### Rate-limit tiers

Each method is bound to its real Slack tier and returns `429` + `Retry-After` +
`{ "ok": false, "error": "ratelimited" }` on breach:

| Tier    | Allowance         | Example methods                            |
| ------- | ----------------- | ------------------------------------------ |
| Tier 1  | \~1 / min         | (heaviest admin-style methods)             |
| Tier 2  | \~20 / min        | `reactions.remove`, `users.setPresence`    |
| Tier 3  | \~50 / min        | `conversations.members`                    |
| Tier 4  | \~100 / min       | most reads; `auth.test` (a high allowance) |
| Special | \~1 / s / channel | `chat.postMessage`                         |

### Control plane (clone-operations — intentionally REST)

Operations that real Slack configures via app manifests / OAuth rather than
runtime Web API methods keep plain REST shapes and are **not** wrapped in the
`{ ok }` envelope:

```
Auth         POST /api/auth/{signup,login,refresh,logout}   GET /api/auth/me
Workspaces   GET/POST /api/workspaces   POST /api/workspaces/:id/join   GET/PUT/DELETE /api/workspaces/:id
Webhooks     POST /api/webhooks/incoming/:id   GET/POST /api/webhooks/{incoming,outgoing}   DELETE …/:id
Events       POST /api/events/subscribe   GET /api/events/subscriptions   DELETE /api/events/subscriptions/:id
Commands     GET/POST /api/commands   DELETE /api/commands/:id   POST /api/commands/execute
Interactions POST /api/interactions   Apps POST/GET /api/apps   Admin POST /api/admin/provision
```

### MCP server

The `mcp-server` exposes the **eight reference Slack MCP tools** with their exact
names and argument names — `slack_list_channels`, `slack_post_message`,
`slack_reply_to_thread`, `slack_add_reaction`, `slack_get_channel_history`,
`slack_get_thread_replies`, `slack_get_users`, `slack_get_user_profile` — with
tool descriptions restored character-for-character to the reference server
([modelcontextprotocol/servers-archived](https://github.com/modelcontextprotocol/servers-archived/tree/main/src/slack)),
plus a few clearly-labeled `slack_*` extensions (`slack_auth_test`,
`slack_search_messages`, `slack_open_dm`). It binds to one `SLACK_MCP_TOKEN`
(`xoxb`/`xoxp`), resolves it to identity + workspace + scopes, and acts strictly
as that identity — gating each tool on its scope and scoping every query to the
token's workspace.

The template ships an `API_PARITY.md` scored by the strict `/verify-api slack`
workflow, which fetches Slack's live docs every run and treats identifier,
envelope, id-format, rate-limit tier, event-schema, and MCP-verbatim conformance
as blocking checks. Check `apps/slack-clone/API_PARITY.md` for the per-method
breakdown.

<Info>
  The [`verify`](/cli/login#verify) command will fold this kind of live fidelity
  scoring into the CLI. Until then, `API_PARITY.md` and the `/verify-api slack`
  workflow are how fidelity is tracked.
</Info>

## Inspect a running clone

```bash theme={null}
asymmetric query slack-a1b2 "select id, name from channels"
asymmetric db slack-a1b2          # prints a psql shell command
asymmetric logs slack-a1b2 -f     # stream the backend
```

The schema (channels, messages, users, workspaces, apps, …) comes straight from
`001_initial_schema.sql` — read it to know what's queryable.
