Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Build an integration bridge

A bridge is a mirror between a ScaiWave room and a channel/room on another system. ScaiWave handles ScaiWave→foreign relay; you handle the foreign→ScaiWave webhook.

1. Plan the mapping#

Decide:

  • Which direction: inbound only, outbound only, or both?
  • Which ScaiWave room ↔ which foreign channel?
  • How are foreign user identities represented? (Most platforms have a user id; you'll use it to dedupe shadow participants.)
  • What's the HMAC scheme on the foreign side? (We'll use that for both directions.)

2. Create the bridge#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X POST "$BASE/v1/admin/bridges" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transport": "custom",
    "direction": "both",
    "room_id": "<scaiwave-room-id>",
    "name": "Acme CRM bridge",
    "outbound_webhook": "https://crm.acme.com/scaiwave/incoming"
  }'

Response includes bridge_id and shared_secret (the HMAC key). Store the secret somewhere safe — it's shown once. Both sides will sign with it.

3. Configure the foreign side#

The foreign side needs to know:

  • The shared secret (for signing incoming webhooks to ScaiWave and verifying outgoing ones).
  • The ScaiWave inbound URL: <host>/v1/bridges/<bridge_id>/inbound.

When the foreign side has a new message, POST to that URL with:

http
1
2
Content-Type: application/json
X-ScaiWave-Bridge-Signature: t=<unix-ts>,v1=<hex-hmac-sha256>

The signature is HMAC-SHA256(shared_secret, f"{ts}.{body}") — the same Stripe-style scheme.

Body:

jsonc
{
  "event_id": "<unique-id-from-foreign-side>",
  "sender": {
    "external_id": "U12345",
    "display_name": "Alice Anderson",
    "avatar_url": "https://crm.acme.com/avatar/U12345.png"
  },
  "content": {
    "msgtype": "swp.text",
    "body": "Hello from Acme CRM"
  },
  "thread_id": null,
  "reply_to_external_id": null
}

event_id is for idempotency — ScaiWave dedupes within a 10-minute window. sender.external_id is what you map to a shadow participant on the ScaiWave side; ScaiWave creates one automatically if needed.

4. ScaiWave creates the event#

When the inbound webhook arrives:

  1. ScaiWave verifies the HMAC. Mismatch → 401, logged.
  2. Looks up or creates a bridge shadow participant for sender.external_id.
  3. Creates an event in the bridge's room with sender_id = shadow.id. The original sender info is embedded in event.content._imported_sender.
  4. The event flows out over WebSocket like any other.

5. Outbound (ScaiWave → foreign)#

Every event in the bridged room is routed through the relay_message_to_bridges ARQ worker. It:

  1. Looks up the bridge config.
  2. Builds a JSON payload (similar shape to inbound).
  3. Signs with the same shared secret.
  4. POSTs to bridge.outbound_webhook.
  5. Retries with exponential backoff on 5xx.

Your endpoint receives:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "event_id": "<scaiwave-event-id>",
  "sender": {
    "fqid": "@alice:scaiwave.example.com",
    "display_name": "Alice"
  },
  "content": {
    "msgtype": "swp.text",
    "body": "Reply from ScaiWave"
  },
  "ts": "2026-05-17T12:00:00Z"
}

Verify the HMAC the same way; if it doesn't match, return 401. Otherwise post into your channel.

6. Slack / Discord / Teams (built-in transports)#

These don't need a custom webhook endpoint on your side; ScaiWave's relay knows their APIs.

For Slack:

json
1
2
3
4
5
6
7
8
9
{
  "transport": "slack",
  "direction": "both",
  "room_id": "<scaiwave-room-id>",
  "slack_team_id": "T0123",
  "slack_channel_id": "C0123",
  "slack_signing_secret": "<slack-signing-secret>",
  "slack_bot_token": "xoxb-…"
}

ScaiWave subscribes to Slack's Events API at /v1/bridges/<id>/slack/events and relays outbound via chat.postMessage. The Slack signing secret lives on your side and in ScaiWave; the bot token is needed for the outbound surface.

7. Operational#

  • Pause a bridge: POST /v1/admin/bridges/{id}/pause. Useful during incident response or maintenance.
  • Rotate the secret: POST /v1/admin/bridges/{id}/rotate-secret. The old secret continues to work for 5 minutes for in-flight messages.
  • Delete: DELETE /v1/admin/bridges/{id}. The bridge's history of shadow participants and events remains; future flow stops.

8. Error handling#

  • HMAC mismatch (inbound) → 401, logged for admin review.
  • Foreign side rate-limited (429) → exponential backoff, retries 3 times. After that the message is marked delivery_failed in the audit log.
  • Foreign side unreachable → retries up to 1 hour, then alert the admin.

Where to go next#

Updated 2026-05-18 12:07:11 View source (.md) rev 2