---
audience: developer
summary: Relay messages between a ScaiWave room and an external chat platform (Slack,
  Discord, Teams, or a remote ScaiWave).
title: Bridges
path: concepts/bridges
status: published
---

# Bridges

A **bridge** is a one-way or two-way mirror between a ScaiWave room
and a room / channel on an external chat platform. Foreign users
appear in the ScaiWave room as `bridge` participants (shadows of
their identity on the source platform); local users appear in the
foreign system as whatever the bridge mapping defines.

## Supported platforms

- **Slack** — first-class. Slack Events API → ScaiWave; ScaiWave
  events → Slack incoming webhook or Bolt app.
- **Discord** — webhook-based.
- **Microsoft Teams** — Graph-based incoming + outgoing webhook.
- **Remote ScaiWave** — for installations that need the
  bridge-shape instead of full federation.

## The shape

A bridge has:

- A **direction**: `in` (foreign → ScaiWave only), `out` (ScaiWave →
  foreign only), or `both`.
- A **transport**: Slack / Discord / Teams / ScaiWave.
- A **room mapping**: which ScaiWave room ↔ which foreign channel.
- A **shared secret** for HMAC-signing the webhook payloads in both
  directions.
- An **active / paused** state. Admins can pause without deleting.

## Setting one up

1. Admin creates the bridge with `POST /v1/admin/bridges`,
   specifying the transport, the ScaiWave room id, and (for outgoing)
   the foreign webhook URL.
2. ScaiWave generates a shared HMAC secret and returns it. Configure
   the foreign side with this secret.
3. Foreign system POSTs to `/v1/bridges/{bridge_id}/inbound` with
   HMAC headers; ScaiWave verifies and creates a `bridge` participant
   shadow for the foreign sender (if not already present), then
   creates the message event in the room.
4. Outgoing: every event in the room is routed through the bridge
   relay ARQ job, which POSTs the foreign system's webhook with the
   right transport's payload shape.

## Identity model

Foreign users are stored as **shadow participants**:

- `participant_type = bridge`.
- `local_id` = sanitised slug of their foreign identifier.
- `display_name` = their foreign display name.
- `avatar_url` = mirrored from the foreign platform when available.

The original sender's identity is also embedded in
`event.content._imported_sender` for export/audit. Shadow participants
**don't** count toward your tenant's participant limit.

## Federation vs. bridges

| Federation | Bridges |
|---|---|
| Symmetric, end-to-end signed | Asymmetric, HMAC-signed webhooks |
| Both sides are ScaiWave | One side can be Slack/Discord/Teams/anything |
| Foreign users have a real account elsewhere | Foreign users are shadows |
| Per-room, opt-in | Per-room, opt-in |
| No transformation | Format translation per platform |
| First-class member experience | Best-effort feature mapping (e.g. Slack threads ≈ ScaiWave threads) |

Choose federation when both sides are ScaiWave; bridges otherwise.

## Failures

- **Slack rate-limit / 429** — outgoing relay backs off exponentially;
  message is retried up to 3 times before being marked failed in the
  audit log.
- **HMAC mismatch** — inbound message is rejected with 401 and logged
  for admin review.
- **Foreign side unreachable** — relay queues; messages drain when
  the foreign side comes back. Admin alert after 1 hour of failure.

## Where to go next

- Tutorial: [Build an integration bridge](/docs/scaiwave/tutorials/developer/build-an-integration-bridge).
- API: [Bridges](/docs/scaiwave/reference/api/bridges).
