Relay is a minimal, streaming-first team chat built on DigitalOcean Droplets, Redis, and SQLite. No integrations nobody uses. No enterprise pricing. Just a clean API and conversations that move fast.
Pricing
A 10-person team on Slack Pro pays $72.50/month. That same team on Relay BYOI rides a $6/month DigitalOcean Droplet — pass-through infra, not per-seat rent. Want zero ops? Relay Cloud is the same transparent DO bill plus 20% — we run it 100% (uptime, backups, patches, upgrades).
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.
Add-on
Off-Droplet backup and replay-friendly archive storage piggybacks on Neon’s published pricing — serverless Postgres, branches, and storage exactly as they list it. Pick who holds the connection string: you, or us.
Paste your Neon Postgres URL into Relay. You pay Neon directly at neon.com/pricing; we route cold archive and DR targets to your project — no markup on their line items.
We provision, monitor, and operate the Neon side — still billed at their public rates, plus 20% for our lifecycle work (scaling cues, failover drills, credential rotation, on-call). Same transparency model as Relay Cloud on the Droplet.
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
Illustrative 500-person org. Slack bar uses common published Business+–style list pricing (~$12.50/user/mo). Relay bar: one Scale Droplet on your account — $48/mo 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.
Bar = 100% — this is the burn you’re normalizing.
~0.77% of the Slack bar — true proportion ($48 ÷ $6,250). Bar is barely visible on purpose.
~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.
$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
$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.
Infrastructure
Your workspace runs on a single Droplet. Start at $4/mo (512 MiB) and scale vertically as you grow. Each workspace gets its own isolated instance.
$4–$96 / moAll live message delivery runs through Redis. Channel subscriptions, presence, typing indicators, and SSE event fanout. Sub-20ms delivery.
Hot pathRecent messages, channel metadata, and user state stored in SQLite on the Droplet's SSD. Blazing-fast local reads. WAL mode for concurrent writes.
Hot storageMessages older than your retention threshold are archived to Neon serverless Postgres. Cold reads on-demand via the same API — zero data loss. Spend follows Neon’s pricing page; optional DR add-on (your connection string vs our +20% managed Neon).
Cold archiveEvery 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-firstRelay'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-compatibleDigitalOcean Tiers
Each tier hosts one Relay workspace. Upgrade by resizing your Droplet — your data stays put. The $6/mo tier handles most small teams comfortably.
| Tier | Memory | vCPUs | SSD | Transfer | Cost | ~Max Users | Cost/User (50 users) | Notes |
|---|---|---|---|---|---|---|---|---|
| Nano | 512 MiB | 1 | 10 GiB | 500 GiB | $4/mo | ~15 | — | Dev / testing |
| Starter | 1 GiB | 1 | 25 GiB | 1,000 GiB | $6/mo | ~30 | $0.20 | ⭐ Most teams start here |
| Standard | 2 GiB | 1 | 50 GiB | 2,000 GiB | $12/mo | ~75 | $0.16 | Small company |
| Growth | 2 GiB | 2 | 60 GiB | 3,000 GiB | $18/mo | ~150 | $0.12 | Active mid-size |
| Pro | 4 GiB | 2 | 80 GiB | 4,000 GiB | $24/mo | ~300 | $0.08 | High-traffic workspace |
| Scale | 8 GiB | 4 | 160 GiB | 5,000 GiB | $48/mo | ~750 | $0.064 | Community / large org |
| Cluster | 16 GiB | 8 | 320 GiB | 6,000 GiB | $96/mo | ~2000+ | $0.048 | Enterprise self-hosted |
Real-time Architecture
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.
Data Architecture
/messages API.API Reference
This documentation was written first. Every endpoint here is the spec. The implementation follows it — not the other way around.
Authorization: Bearer <RELAY_API_KEY>All requests require a Bearer token. Tokens are workspace-scoped — generate from the dashboard or via /auth/token.
Create a new workspace. Provisions an isolated namespace, default channels, and an admin user. Returns the workspace object with provisioning status.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Unique workspace slug (lowercase, hyphens OK) |
| display_name | string | required | Human-readable workspace name |
| plan | enum | optional | starter | standard | pro | scale |
| admin_email | string | required | Email of initial workspace admin |
| region | string | optional | DO datacenter region slug (e.g. nyc3, sfo3) |
{
"name": "acme-corp",
"display_name": "Acme Corp",
"plan": "starter",
"admin_email": "tyler@acme.com",
"region": "nyc3"
}{
"id": "ws_01HXYZ4RELAY",
"name": "acme-corp",
"plan": "starter",
"region": "nyc3",
"created_at": "2026-04-28T09:00:00Z",
"status": "provisioning",
"api_key": "rly_live_sk_...",
"stream_url": "https://ws_01HXYZ4RELAY.relay.dev/v1/stream"
}{
"error": "validation_error",
"message": "Workspace name already taken",
"field": "name"
}Retrieve workspace details by ID. Includes provisioning status, plan, region, and member count.
| Path Param | Type | Required | Description |
|---|---|---|---|
| id | string | required | Workspace ID (ws_...) |
List all channels in the workspace. Supports filtering by type (public/private/dm) and membership.
| Query Param | Type | Required | Description |
|---|---|---|---|
| type | enum | optional | public | private | dm |
| member_of | boolean | optional | Only return channels the authenticated user belongs to |
| cursor | string | optional | Pagination cursor from previous response |
| limit | integer | optional | Results per page (default: 50, max: 200) |
Create a new channel. Public channels are discoverable by all workspace members. Private channels require explicit membership.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Channel name (lowercase, hyphens, no spaces) |
| type | enum | optional | public (default) | private |
| description | string | optional | Channel purpose/description |
| member_ids | string[] | optional | Initial member user IDs |
Join a public channel. For private channels, membership must be granted by a channel admin.
| Path Param | Type | Required | Description |
|---|---|---|---|
| id | string | required | Channel ID (ch_...) |
Post a message to a channel or DM thread. Supports plain text, Markdown, file attachments, and structured blocks. Message is instantly fanned out via Redis Pub/Sub to all connected subscribers.
| Parameter | Type | Required | Description |
|---|---|---|---|
| channel_id | string | required | Target channel ID (ch_...) or DM ID (dm_...) |
| text | string | required* | Message body. Markdown supported. *Required if blocks not provided |
| blocks | Block[] | optional | Structured message blocks (see Block Schema) |
| thread_id | string | optional | Reply in a thread by referencing a parent message ID |
| attachments | Attachment[] | optional | File or link attachments |
| metadata | object | optional | Arbitrary JSON metadata (stored, queryable) |
{
"channel_id": "ch_01HXYZ",
"text": "Deploy summary",
"blocks": [
{ "type": "header", "text": "v2.1 deployed successfully" },
{ "type": "section", "text": "Environment: **production**\nDuration: 42s\nStatus: ✅" },
{ "type": "actions", "elements": [
{ "type": "button", "text": "View logs", "url": "https://logs.acme.com" }
]}
]
}Retrieve messages for a channel. Hot messages are served from SQLite. Queries spanning the archive threshold are transparently merged with Neon Postgres results.
| Query Param | Type | Required | Description |
|---|---|---|---|
| channel_id | string | required | Target channel or DM ID |
| before | ISO 8601 | optional | Fetch messages before this timestamp |
| after | ISO 8601 | optional | Fetch messages after this timestamp |
| limit | integer | optional | Max messages returned (default: 50, max: 500) |
| include_threads | boolean | optional | Include threaded replies inline |
Edit the content of an existing message. Only the message author or workspace admins may edit. Edits are recorded in the message's edit_history.
| Parameter | Type | Required | Description |
|---|---|---|---|
| text | string | optional | New message text |
| blocks | Block[] | optional | Updated structured blocks |
Soft-delete a message. The message is replaced with a tombstone in the channel. Permanently deleted after the workspace retention period.
| Path Param | Type | Required | Description |
|---|---|---|---|
| id | string | required | Message ID (msg_...) |
Send an invitation email to a new workspace member. Generates a one-time invite token valid for 7 days.
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | required | Email address to invite | |
| role | enum | optional | member (default) | admin |
| channel_ids | string[] | optional | Auto-join these channels on first login |
Returns presence status for all or specified workspace members. Backed by Redis TTL keys — refreshed every 30s by connected clients.
| Query Param | Type | Required | Description |
|---|---|---|---|
| user_ids | string | optional | Comma-separated user IDs (omit for all members) |
Open a Server-Sent Events stream for the workspace. The client receives a real-time feed of all events the authenticated user is permitted to receive. Redis Pub/Sub fans out internally; clients see a clean SSE stream.
| Query Param | Type | Required | Description |
|---|---|---|---|
| workspace_id | string | required | Target workspace ID |
| channels | string | optional | Comma-separated channel IDs to filter stream |
| events | string | optional | Comma-separated event types (default: all) |
| last_event_id | string | optional | Resume from last received event (SSE standard) |
// message.created
data: {"type":"message.created","id":"evt_01H...","data":{"message":{...}}}
// typing.start / typing.stop
data: {"type":"typing.start","id":"evt_01H...","data":{"user_id":"usr_...","channel_id":"ch_..."}}
// presence.update
data: {"type":"presence.update","id":"evt_01H...","data":{"user_id":"usr_...","status":"online"}}
// ai.response (streaming token)
data: {"type":"ai.response","id":"evt_01H...","data":{"bot_id":"bot_...","token":"Hello","done":false}}Register an AI bot or integration participant. Bots receive messages via webhook and can post back using the standard messages API. Tool schemas follow the OpenAI function-calling format.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Bot display name |
| webhook_url | string | required | HTTPS endpoint to receive message events |
| tools | Tool[] | optional | OpenAI-compatible tool/function schemas the bot exposes |
| channels | string[] | optional | Channel IDs the bot is a member of |
| mention_only | boolean | optional | Only trigger webhook on @mention (default: false) |
{
"name": "deploy-bot",
"webhook_url": "https://your-server.com/relay-hook",
"mention_only": true,
"tools": [
{
"type": "function",
"function": {
"name": "get_deployment_status",
"description": "Get the current deployment status for an environment",
"parameters": {
"type": "object",
"properties": {
"environment": { "type": "string", "enum": ["production","staging","dev"] }
},
"required": ["environment"]
}
}
}
]
}Returns the OpenAI-compatible tool schema array for a registered bot. Used by AI agents to discover callable functions in a channel.
Register an outbound webhook. Relay will POST event payloads to your endpoint when specified events occur. HMAC-SHA256 signed with your webhook secret.
| Parameter | Type | Required | Description |
|---|---|---|---|
| url | string | required | HTTPS endpoint to receive webhook payloads |
| events | string[] | required | Event types to subscribe to (e.g. ["message.created","reaction.added"]) |
| channel_ids | string[] | optional | Scope webhook to specific channels |
| secret | string | optional | HMAC signing secret for payload verification |
Integrations
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.
GPT-4o-powered assistant in every workspace. @mention to ask questions, summarize threads, draft replies, or run custom tools.
HMAC-signed outbound webhooks for any event. The foundation for every integration you'll ever build.
Post PR, issue, and deployment events to channels. Open-source template — deploy in minutes on your own infra.
Route on-call alerts to Relay channels. Acknowledge directly from the API. Template available.
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.