API-first · streaming · pass-through infrastructure

Team messaging.
Actually affordable.

Relay is a minimal, streaming-first team chat. One server per workspace, your data on your terms. No integrations nobody uses. No enterprise pricing. Just a clean API and conversations that move fast.

dev3lop.com/relay-api / quickstart
# Sign in (GitHub OAuth, sets session cookie)
GET https://dev3lop.com/auth/github?next=/relay/app/lobby

# Send a message to a room
POST https://dev3lop.com/relay-api/rooms/lobby/messages
{
  "content": "Hello, world.",
  "channelId": null
}

# Stream room events (SSE)
GET https://dev3lop.com/relay-api/rooms/lobby/stream

✓ 201 Created — Redis fan-out < 20ms

Slack charges per seat.
We charge per server.

A 10-person team on Slack Pro pays $72.50/month. That same team on Relay BYOI rides a single small server in the single-digit dollars per month — pass-through infra, not per-seat rent. Want zero ops? Relay Cloud is the same transparent server bill plus 20% — we run it 100% (uptime, backups, patches, upgrades). Talk to us for sizing and the underlying numbers.

Slack Pro $7.25/user/mo
Published list · per seat · their cloud only
Relay · one workspace, your choice

The default team chat playbook: every hire raises the tab, history and APIs follow paid tiers, and there is no self-hosted escape hatch — you rent the app, the roadmap, and the integration marketplace forever.

Relay BYOI $6/server/mo Your server, your bill, your keys. We ship Relay; you own the box — pass-through infrastructure pricing, no markup.
Relay Cloud $7.20/server/mo Same pass-through server price × 1.2. We run it 100% — monitoring, backups, patches, upgrades. You chat; we operate.
10 users $72.50 / mo
10 users $6 / mo $7.20 / mo
25 users $181.25 / mo
25 users $6–$12 / mo $7.20–$14.40 / mo
50 users $362.50 / mo
50 users $12 / mo $14.40 / mo
100 users $725.00 / mo
100 users $18–$24 / mo $21.60–$28.80 / mo
Message history 90 days (free tier)
Message history Unlimited (SQLite)
API access Restricted on free
API access Full access, always
Integration cost Baked into pricing
Integration cost Build your own, free
Who runs the server Slack · their cloud only
Who runs the server You · your infrastructure Relay · fully managed

Monthly burn · same headcount

Slack bills per seat. Relay bills per server.

Relay · tap to compare
Slack Pro — $7.25/user/mo list · every teammate multiplies the bill. Bars below: Slack = full width; Relay = actual share of that Slack total (same row).
Relay BYOI $6/server/mo Pass-through server pricing. You operate the box.
Relay Cloud $7.20/server/mo Pass-through × 1.2 — we run it 100%.
Slack total Relay total

Shared axis on every row: $0–$725/mo (100 people × $7.25 Slack Pro list). Bar length = that dollar amount ÷ $725.

10 users
Slack
$72.50
Relay
$6
Relay
$7.20
25 users
Slack
$181.25
Relay
$6–$12
Relay
$7.20–$14.40
50 users
Slack
$362.50
Relay
$12
Relay
$14.40
100 users
Slack
$725
Relay
$18–$24
Relay
$21.60–$28.80

25 & 100-seat Relay bars use the midpoint of the server tier range vs Slack list. Ranges are labeled on the bar.

Also not even close

Message history 90 days ✓ Unlimited
API access Gated ✓ Always on
Integration cost In the rent ✓ Build your own
Who runs the server Slack only ✓ You✓ Relay ops

Add-on

Disaster recovery · off-server cold archive

An optional managed Postgres archive sits behind your room API for the long tail of message history, plus point-in-time recovery snapshots. Pick who holds the connection string: you, or us. Pricing and the underlying provider are part of the conversation when you sign on — get in touch for a quote.

Bring your own Postgres

Your connection string

Paste a Postgres URL into Relay. You pay your provider directly; we route cold archive and DR targets to your database — no markup on the underlying line items.

Relay managed · +20%

We run it for you

We provision, monitor, and operate the archive side — pass-through pricing on the underlying database plus 20% for our lifecycle work (scaling cues, failover drills, credential rotation, on-call). Same transparency model as Relay Cloud on the chat tier.

Optional on every workspace. Stacks with Relay BYOI or Relay Cloud — flip the toggle above; this add-on is orthogonal.

While we’re here: enterprise Slack is a procurement saga, a SSO tax, a 90-day history ransom on “free,” and a marketplace of integrations you could ship yourself before the kickoff call ends. Relay is chat infrastructure — not a platform rent-seeking on every seat.

Same headcount · different physics

Enterprise Slack isn’t priced like software — it’s priced like a second office lease.

Illustrative 500-person org. Slack bar uses common published Business+–style list pricing (~$12.50/user/mo). Relay bar: one Scale-tier server on your infrastructure — $48/mo illustrative list, whole workspace. Your Slack contract may differ; the shape of the chart won’t.

Illustrative 500-person org. Slack bar uses common published Business+–style list pricing (~$12.50/user/mo). Relay bar: same box fully managed — list $48/mo × 1.2 = $57.60/mo. Your Slack contract may differ; the shape of the chart won’t.

Slack · 500 × $12.50 list $6,250 / mo

Bar = 100% — this is the burn you’re normalizing.

Relay BYOI · Scale-tier server (500 seats) $48 / mo

~0.77% of the Slack bar — true proportion ($48 ÷ $6,250). Bar is barely visible on purpose.

Relay Cloud · same box, fully managed (+20%) $57.60 / mo

~0.92% of the Slack bar — coffee budget for one VP; chat for five hundred people.

Baseline Slack burn here: $6,250/mo (500 × $12.50 list). For Relay BYOI at $48/mo: monthly difference (Slack − Relay), yearly cash kept (×12), and percent vs that Slack line — plus the ratio.

Baseline Slack burn here: $6,250/mo (500 × $12.50 list). For Relay Cloud at $57.60/mo: monthly difference (Slack − Relay), yearly cash kept (×12), and percent vs that Slack line — plus the ratio.

Relay BYOIYour server · $48/mo

$6,202/mo less than Slack (monthly difference)

$74,424/yr total not spent vs Slack, same headcount

99.23% lower than Slack · Relay is 0.77% of Slack’s tab · ~130.2× cheaper

Relay CloudFully managed · $57.60/mo

$6,192.40/mo less than Slack (monthly difference)

$74,308.80/yr total not spent vs Slack, same headcount

99.08% lower than Slack · Relay is 0.92% of Slack’s tab · ~108.5× cheaper

Checks: $6,250 − $48 = $6,202; ×12 = $74,424; savings ÷ $6,250 = 99.23%. Ratio: $6,250 ÷ $48 ≈ 130.2×. Not financial advice — illustrative list math.

Checks: $6,250 − $57.60 = $6,192.40; ×12 = $74,308.80; savings ÷ $6,250 = 99.08%. Ratio: $6,250 ÷ $57.60 ≈ 108.5×. Not financial advice — illustrative list math.

Built on primitives.
Nothing you don't need.

01 — Compute

Single-tenant server

Your workspace runs on a single server. Pick a starter size and scale vertically as you grow — each workspace gets its own isolated instance. Sizing and provider are part of the onboarding conversation.

One workspace per box
02 — Real-time

Redis Pub/Sub

All live message delivery runs through Redis. Channel subscriptions, presence, typing indicators, and SSE event fanout. Sub-20ms delivery.

Hot path
03 — Persistence

SQLite (hot)

Recent messages, channel metadata, and user state stored in SQLite on the server's local SSD. Blazing-fast local reads. WAL mode for concurrent writes.

Hot storage
04 — Archive

Cold Postgres archive

Messages older than your retention threshold are archived to a managed Postgres database. Cold reads on-demand via the same API — zero data loss. Optional DR add-on lets you bring your own connection string or have us run it for you.

Cold archive
05 — Interface

REST + SSE API

Every feature is a first-class API endpoint. Build your own client, automate workflows, or integrate with anything. OpenAI-compatible tool schemas for AI agents.

API-first
06 — Intelligence

AI-ready Tooling

Relay's API follows OpenAI function-calling conventions. Drop an AI agent into any channel as a participant. Webhooks, tool schemas, and structured responses built in.

OpenAI-compatible

One server per workspace.
Scale when you need to.

Most small teams run comfortably on a single low-end server. As your team grows, you resize the box vertically — your data stays put, no migration. Picking the right starting size, the right provider, and the right region is part of the onboarding conversation. Reach out for a sizing recommendation tailored to your team and traffic profile.

Redis-first streaming.
Events, not polling.

Clients subscribe to an SSE stream. Redis Pub/Sub fans out events across all connected clients in a workspace. No WebSocket complexity — just a persistent HTTP connection.

14:32:01.042 message.created channel=#engineering  author=tyler  text="just deployed v2.1"
14:32:01.089 presence.update user=sarah  status=online  workspace=ws_01HXYZ
14:32:02.310 typing.start channel=#engineering  user=sarah
14:32:04.881 message.created channel=#engineering  author=sarah  text="🎉 nice work"
14:32:04.883 reaction.added message=msg_01HABC  emoji=🚀  user=marcus
14:32:05.001 ai.response bot=relay-bot  channel=#engineering  streaming=true

Hot-cold storage.
Reads always fast. History never lost.

Client Web, mobile, or API consumer sends message
API Layer Validates auth, parses request, assigns IDs
Redis Publishes to channel subscribers instantly
SQLite Persists to local hot store (WAL mode)
Cold archive Older history archived to managed Postgres
Hot Path (Redis)
All active channel messages live in Redis for instant Pub/Sub fanout. TTL-based eviction keeps memory lean. Presence and typing use ephemeral keys.
Warm Store (SQLite)
Local SSD SQLite in WAL mode handles concurrent reads and writes. Recent history (configurable 30–90 days) lives here. Zero network overhead for reads.
Cold Archive
A managed Postgres database archives the full message history. Queries against archived data are served transparently through the same room messages API.

The full API.
Mapped to what's actually shipped.

Every endpoint below is mounted in the running Hono server in apps/agents. Public traffic enters at https://dev3lop.com/relay-api/* and is reverse-proxied to the workspace's chat tier. Programmatic API tokens for non-browser clients are on the roadmap; today the same browser session cookie issued by GitHub OAuth carries every call.

Authentication Cookie: relay_session=<signed>  ·  obtained via GitHub OAuth

Open /auth/github?next=/relay/app/<slug> to start the OAuth dance. The callback at /auth/callback upserts your GitHub profile and sets a 30-day signed session cookie. Every /relay-api/* request reuses that cookie — there is no separate Bearer key today.

Sets a short-lived signed state cookie and 302s the browser to github.com/login/oauth/authorize. After the user approves, GitHub redirects to /auth/callback, which exchanges the code, upserts the GitHub profile in SQLite, and writes a 30-day signed session cookie.

Query ParamTypeRequiredDescription
nextstringoptionalPath to land on after callback. Must be same-origin and not under /auth/; defaults to the lobby room.

Reads the session cookie and returns the authenticated user's profile (id, githubId, login, name, avatarUrl, email). Responds 401 with null body when no valid session is attached.

Removes the signed session cookie. The user record stays in SQLite — the next OAuth round trip will reuse it.

Resolves the seeded default room (the lobby) for the signed-in user. Used by the SPA on first load to know where to send people who haven't picked a room yet.

Returns the room's branding, owner, and settings. Whitelisted rooms reject non-members with 403; the lobby is always public.

Owners can rename the room (collision-checked against reserved slugs), update its display name, description, accent colors, and avatar. Updates are broadcast over Redis pub/sub so every connected member's UI updates live.

Flips the room between open and whitelist-only. When enabled, only allow-listed GitHub logins or verified emails can read/write; everyone else hits the access-request flow.

Returns channels in the room — public ones plus any private/group channels and DMs the caller is a member of. Each row carries id, slug, name, visibility, memberCount, and createdBy.

Owner-only. The lobby has no admin, so its create-channel UI is hidden and this endpoint refuses calls there. visibility is public | private | group | personal; inviteLogins[] seeds co-admin members.

Request
{
  "name": "Engineering",
  "slug": "engineering",
  "visibility": "public",
  "inviteLogins": ["alice", "bob"]
}

Available to anyone with room access. Creates (or reuses) a private DM channel between the caller and the supplied GitHub logins. Returns the channel DTO and broadcasts a direct_created event so the other parties' tabs add it without refresh.

Allowed if the caller owns the room or created the channel. Wipes the channel's messages from SQLite and publishes a deleted event to all subscribers.

Returns every user who has spoken in or been invited to the room. Used by the mention picker and the DM flow.

Reads from SQLite (the hot store). Use channelId=__lobby for the room-wide thread, a real channel id to scope to one channel, or omit to get everything visible. Pagination uses a (beforeCreatedAt, beforeId) keyset so concurrent inserts can't shift pages.

Query ParamTypeRequiredDescription
channelIdstringoptionalChannel id, __lobby, or omit for all
beforeCreatedAtISO 8601optionalUsed with beforeId for keyset pagination
beforeIdstringoptionalTiebreaker for messages with identical timestamps
limitintegeroptionalDefault 20, capped at 100

The HTTP send path mirrors the WebSocket path: it builds the message row, publishes to Redis first (so other tabs see it in one RTT), then persists to SQLite asynchronously. Mentions are processed off the response path. Content is trimmed and capped at 4,000 characters.

Request
{
  "content": "Deploy looks green. ",
  "channelId": "ch_8fd2…"
}

Only the original author may edit. Updated body is published as a message_updated event so connected clients can rerender in place.

Author or room owner. Removes the row from SQLite and publishes a message_deleted event with the channel id so clients can drop it from the relevant view.

The primary live transport. On connect the server replays the last 20 messages as a sync frame, then forwards every Redis pub/sub event for the room. Clients send {"type":"send","clientMsgId","content","channelId"} frames; the server fans out via Redis and acks on the same socket so optimistic UI can promote in place.

Frames
// server → client (initial)
{ "type": "sync", "messages": [...] }

// client → server
{ "type": "send", "clientMsgId": "c_42", "content": "hi", "channelId": null }

// server → client (echo on every connected tab)
{ "type": "message", "clientMsgId": "c_42", "message": { ... } }

// server → sender only
{ "type": "ack", "clientMsgId": "c_42", "serverId": "msg_…", "createdAt": "…" }

Read-only SSE stream for environments where WebSockets are blocked. Sends an initial sync with the last 20 messages, then relays every message, message_updated, message_deleted, and channel/branding event the room publishes. A ping event fires every 20 seconds to keep proxies open.

Event types
event: room
data: {"type":"sync","messages":[ ... ]}

event: room
data: {"type":"message","message":{ ... }}

event: room
data: {"type":"message_updated","message":{ ... }}

event: room
data: {"type":"message_deleted","messageId":"msg_…","channelId":"ch_…"}

event: ping
data:

Returns every login or email allowed into a gated room.

Owner-only. Accepts a GitHub login or an email address and a free-form note.

Owner-only. The user keeps any session they already have until it expires, but new joins are blocked.

Owner-only. Returns the queue of would-be members the room hasn't yet approved or denied.

Any signed-in user can call this against a gated room. The request becomes a notification on the owner's dashboard.

Owner-only. Marks the request approved and inserts the requester into the room's whitelist atomically.

Owner-only. The requester is not notified beyond the existing dashboard state change.

Returns mentions, access requests, and other room-scoped notifications addressed to the caller.

Updates the notification's lifecycle state.

Pin a notification so it shows up in the saved tab regardless of read state.

Used by room owners to mark a notification public or private inside the room.

Idempotent. Returns the existing thread if one already exists, otherwise creates a new one and returns its id.

Returns thread replies in chronological order with author profile data joined in.

Posts a reply on behalf of the caller. Thread replies aren't broadcast on the room socket — clients refetch the thread on focus.

Returns the first board for a room. Creates a default Todo · Doing · Done board the first time it's hit.

Spins up another board with default columns alongside the room's first one.

Returns the board with its columns and cards inlined.

Drops the new card into the board's first column.

Updates the card's column and position. The board owner can move any card; other members can only move their own.

Saved cards float to the top of their column for the caller.

Multipart form upload. Images are converted to WebP under 70 KB before storage. Bind the result to a message or other entity by sending entityType + entityId in the same form.

Streams the stored bytes back with the right content-type. Access is gated by the entity the attachment belongs to.

Backed by Redis TTL keys. Connected clients heartbeat every 30 seconds; stale entries fall off automatically.

Bumps the caller's presence key and broadcasts an online event if they were stale.

Used by the AI add-on's private chat surface. Publishes a short-lived typing event for the named conversation.

Unauthenticated. Pings Redis and returns a small JSON document used by the workspace server's external uptime probe.

Response
{
  "ok": true,
  "ts": "2026-05-04T23:30:00.000Z",
  "checks": { "server": "ok", "redis": "ok" }
}
On the roadmap

Programmatic API tokens (so bots and CI pipelines can auth without a browser session), HMAC-signed outbound webhooks, and a public bot registration endpoint with OpenAI-compatible tool schemas. The architecture already supports them — Redis pub/sub fan-out, mention-driven webhooks, and the AI add-on are all wired up internally — they just don't have stable public routes yet.

No bloat.
Add exactly what you need.

AI makes integrations trivial to build.
We don't charge for them.

Slack's pricing model bundles integrations you'll never use into the cost of messaging. In 2026, connecting two services is a 10-minute GPT task. Relay provides the webhook surface and the bot API — you own the integrations.

Want us to build one? We can. Want to build it yourself? The API is everything you need. The architecture is intentionally open-ended: bots are just webhook listeners that speak the Relay message format.

There's no app directory to unlock, no per-seat fee for GitHub notifications, no enterprise tier required to connect your CI pipeline. Register a bot, point it at your server, and write 20 lines of code.

relay-bot (AI) Built-in

GPT-4o-powered assistant in every workspace. @mention to ask questions, summarize threads, draft replies, or run custom tools.

Webhooks Built-in

HMAC-signed outbound webhooks for any event. The foundation for every integration you'll ever build.

GitHub / GitLab Community

Post PR, issue, and deployment events to channels. Open-source template — deploy in minutes on your own infra.

PagerDuty / alerts Community

Route on-call alerts to Relay channels. Acknowledge directly from the API. Template available.

Your integration Custom

Register a bot, point a webhook at your server, use the /bots API. You build it in an afternoon. We're here if you need help.