Documentation
Everything you need to integrate real-time features with AltoHost.
On this page
Quick Start
Get real-time events flowing in four steps.
1. Create an account
Sign up at app.altohost.com. Create your first app to get credentials.
2. Set up environment variables
Add the following to your .env file:
ALTOHOST_APP_ID=your-app-id
ALTOHOST_KEY=your-app-key
ALTOHOST_SECRET=your-app-secret
ALTOHOST_HOST=ws.altohost.com
ALTOHOST_PORT=4433. Install SDKs
# Server
npm install @girardmedia/altohost-node
# Client
npm install @girardmedia/altohost-js4. Send your first event
Trigger an event from the server and listen for it on the client.
import { AltoHost } from '@girardmedia/altohost-node'
const alto = new AltoHost({
appId: process.env.ALTOHOST_APP_ID,
key: process.env.ALTOHOST_KEY,
secret: process.env.ALTOHOST_SECRET,
host: process.env.ALTOHOST_HOST,
})
await alto.trigger('notifications', 'new-alert', {
title: 'New message',
body: 'You have a new notification'
})import AltoHost from '@girardmedia/altohost-js'
const alto = new AltoHost('your-app-key', {
wsHost: 'ws.altohost.com',
})
const channel = alto.subscribe('notifications')
channel.on('new-alert', (data) => {
showNotification(data.title, data.body)
})Environment Variables
Configure these variables in your server and client environments.
| Variable | Description | Required |
|---|---|---|
| ALTOHOST_APP_ID | Your application ID (found in dashboard) | Required |
| ALTOHOST_KEY | Your API key | Required |
| ALTOHOST_SECRET | Your API secret (server-side only, never expose to client) | Required (server) |
| ALTOHOST_HOST | WebSocket server host (ws.altohost.com) | Optional |
| ALTOHOST_PORT | WebSocket server port (443) | Optional |
ALTOHOST_SECRET in client-side code. The secret is only needed for server-side operations like triggering events and authenticating private channels.Server SDK
The altohost-node package provides a server-side SDK for Node.js, Deno, and edge runtimes.
Initialization
import { AltoHost } from '@girardmedia/altohost-node'
const alto = new AltoHost({
appId: process.env.ALTOHOST_APP_ID,
key: process.env.ALTOHOST_KEY,
secret: process.env.ALTOHOST_SECRET,
host: process.env.ALTOHOST_HOST,
})alto.trigger(channel, event, data)
Send an event to all subscribers of a channel. Supports single channels or an array of channel names.
// Single channel
await alto.trigger('chat-room', 'new-message', {
user: 'alice',
text: 'Hello!',
})
// Multiple channels
await alto.trigger(
['chat-room-1', 'chat-room-2'],
'new-message',
{ text: 'Broadcast!' }
)alto.triggerBatch(events)
Send multiple events in a single API call for better performance.
await alto.triggerBatch([
{
channel: 'user-1',
event: 'notification',
data: { title: 'New message' }
},
{
channel: 'user-2',
event: 'notification',
data: { title: 'Order shipped' }
}
])alto.authenticateChannel(socketId, channel)
Generate an HMAC-SHA256 signed auth response for private and presence channels.
// Private channel
const auth = alto.authenticateChannel(socketId, channelName)
// Returns: { auth: "key:signature" }
// Presence channel — include user data
const auth = alto.authenticateChannel(socketId, channelName, {
user_id: user.id,
user_info: { name: user.name, avatar: user.avatar }
})alto.getChannelInfo(channel)
Get detailed information about a specific channel.
const info = await alto.getChannelInfo('presence-lobby')
console.log(info.occupied) // true
console.log(info.user_count) // 5
console.log(info.subscription_count) // 7alto.getPresenceMembers(channel)
Get the list of users currently subscribed to a presence channel.
const users = await alto.getPresenceMembers('presence-lobby')
console.log(users) // { users: [{ id: "user_1" }, { id: "user_2" }] }Client SDK
The altohost-js package is a zero-dependency WebSocket client for browsers. TypeScript declarations included.
Initialization
import AltoHost from '@girardmedia/altohost-js'
const alto = new AltoHost('your-app-key', {
wsHost: 'ws.altohost.com',
authEndpoint: '/api/altohost/auth', // for private/presence channels
})alto.subscribe(channel)
Subscribe to a channel and return a channel object you can attach listeners to.
const channel = alto.subscribe('news')channel.on(event, callback)
Listen for events on a channel. Also available as .bind() for convenience.
channel.on('breaking', (data) => {
console.log(data.headline)
})channel.off(event, callback)
Remove an event listener. Also available as .unbind() for convenience.
// Remove a specific handler
channel.off('breaking', myHandler)
// Remove all handlers for an event
channel.off('breaking')alto.unsubscribe(channel)
Unsubscribe from a channel.
alto.unsubscribe('news')alto.disconnect()
Disconnect from the WebSocket server and clean up all subscriptions.
alto.disconnect()Channels
Channels are the fundamental unit of real-time communication. There are three types.
Public Channels
Any name without a prefix. Anyone can subscribe without authentication. Ideal for broadcast data like live scores, stock tickers, or public announcements.
const channel = alto.subscribe('live-scores')
channel.on('score-update', (data) => {
updateScoreboard(data)
})Private Channels
Prefix with private-. Requires server-side authentication. The client SDK automatically sends a POST request to your auth endpoint.
const channel = alto.subscribe('private-user-123')
channel.on('notification', (data) => {
showNotification(data)
})Presence Channels
Prefix with presence-. Like private channels but also track who is subscribed. Perfect for "who's online" features, collaborative editing, and live user counts.
const channel = alto.subscribe('presence-room-1')
channel.on('member_added', (member) => {
console.log(member.info.name, 'joined')
})Authentication
Private and presence channels require server-side authentication. When a client subscribes to a private- or presence- channel, the SDK sends a POST request to your auth endpoint with the socket_id and channel_name.
Server-side auth endpoint
Create an API route that verifies the user is authorized, then returns a signed auth token.
import { AltoHost } from '@girardmedia/altohost-node'
const alto = new AltoHost({
appId: process.env.ALTOHOST_APP_ID,
key: process.env.ALTOHOST_KEY,
secret: process.env.ALTOHOST_SECRET,
host: process.env.ALTOHOST_HOST,
})
export async function POST(request) {
const { socket_id, channel_name } = await request.json()
// Verify the user is authorized (your own logic)
const auth = alto.authenticateChannel(socket_id, channel_name)
return Response.json(auth)
}Client configuration
Point the client SDK to your auth endpoint.
const alto = new AltoHost('your-app-key', {
wsHost: 'ws.altohost.com',
authEndpoint: '/api/altohost/auth',
})Presence
Presence channels let you track which users are currently subscribed. You get join/leave events and can query the full member list at any time.
Subscribing with user data
Your auth endpoint must return user data for presence channels. See the Authentication section above.
const presence = alto.subscribe('presence-room-1')
// Fired once when subscription succeeds — contains current member list
presence.on('subscription_succeeded', (members) => {
console.log('Online:', members)
})
// Fired when a new member joins
presence.on('member_added', (member) => {
console.log(member.info.name, 'joined')
})
// Fired when a member leaves
presence.on('member_removed', (member) => {
console.log(member.info.name, 'left')
})Client Events
Client events let connected clients send messages directly to each other without a server roundtrip. They must be sent on a private or presence channel, and the event name must start with client-.
Typing indicators example
const channel = alto.subscribe('private-conversation-456')
// Send a typing indicator to other users
channel.trigger('client-typing', {
userId: 'user-123',
isTyping: true,
})
// Listen for typing from others
channel.on('client-typing', (data) => {
showTypingIndicator(data.userId, data.isTyping)
})Webhooks
Configure a webhook URL in your app settings to receive real-time notifications about channel and presence events on your server.
Webhook events
| Event | Description |
|---|---|
| channel_occupied | First subscriber joins a channel |
| channel_vacated | Last subscriber leaves a channel |
| member_added | User joins a presence channel |
| member_removed | User leaves a presence channel |
Webhook payload
Webhook requests are sent as POST with a JSON body containing a time_ms timestamp and an events array.
{
"time_ms": 1711622400000,
"events": [
{
"name": "channel_occupied",
"channel": "chat-room"
}
]
}Signature verification
Every webhook includes an X-AltoHost-Signature header — an HMAC-SHA256 hex digest of the raw body signed with your webhook secret. Always verify signatures to ensure the request came from AltoHost.
import crypto from 'crypto'
const WEBHOOK_SECRET = process.env.ALTOHOST_WEBHOOK_SECRET
export async function POST(request) {
const rawBody = await request.text()
const signature = request.headers.get('x-altohost-signature')
// Verify HMAC-SHA256 signature
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(rawBody)
.digest('hex')
if (signature !== expected) {
return Response.json(
{ error: 'Invalid signature' },
{ status: 401 }
)
}
const body = JSON.parse(rawBody)
for (const event of body.events) {
switch (event.name) {
case 'channel_occupied':
console.log('Channel active:', event.channel)
break
case 'member_added':
console.log('User joined:', event.user_id)
break
}
}
return Response.json({ ok: true })
}Delivery headers
| Header | Description |
|---|---|
| X-AltoHost-Signature | HMAC-SHA256 hex digest of the raw request body |
| X-AltoHost-Delivery | Unique delivery ID (for deduplication) |
| X-AltoHost-Attempt | Delivery attempt number (1, 2, or 3) |
Retry policy
Failed deliveries (non-2xx response or timeout) are retried up to 3 times with exponential backoff: 1 second, 5 seconds, 30 seconds. Delivery attempts timeout after 10 seconds. You can monitor delivery status in the dashboard under your app's webhook logs.
API Tokens
Use API tokens for programmatic access to the AltoHost API. Tokens use Bearer authentication and work with all API endpoints. Create tokens in Settings → API Tokens.
Using API Tokens
Include the token in the Authorization header:
curl -H "Authorization: Bearer alto_your_token_here" \\
https://app.altohost.com/api/appsToken Scopes
| Scope | Permissions |
|---|---|
| apps:read | List and view apps, keys, channels, usage |
| apps:write | Create, update, and delete apps plus non-secret app settings |
| events:trigger | Trigger events and use debug tools |
| secrets:manage | View app secrets, rotate webhook secrets, and manage app key credentials |
| tokens:manage | Create and revoke API tokens |
secrets:manage for systems that truly need credential rotation or secret reads.Event History
Query recent channel events for your app via the history API. Useful for replaying missed events, building audit logs, and debugging.
Query events
# Get recent events for a channel
curl 'https://app.altohost.com/api/apps/YOUR_APP_ID/history?channel=chat-room&limit=50' \
-H 'Authorization: Bearer alto_YOUR_TOKEN'Query parameters
| Parameter | Type | Description |
|---|---|---|
| channel | string | Filter by channel name |
| event | string | Filter by event type (CONNECT, SUBSCRIBE, CLIENT_EVENT, etc.) |
| since | ISO 8601 | Return events after this timestamp |
| until | ISO 8601 | Return events before this timestamp |
| limit | number | Max events to return (default 100, max 500) |
Response format
{
"events": [
{
"id": "clx1...",
"channel": "chat-room",
"event": "SUBSCRIBE",
"socketId": "1234.5678",
"metadata": { "userId": "user_1" },
"createdAt": "2026-03-28T12:00:00.000Z"
}
],
"count": 1,
"hasMore": false
}REST API Reference
All dashboard and account management APIs. Authentication is via session cookies (set by the login endpoint).
Authentication
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register | Create account (name, email, password) |
| POST | /api/auth/login | Login, sets session cookie |
| POST | /api/auth/logout | Destroy session |
| GET | /api/auth/me | Current account info |
| PUT | /api/auth/update-profile | Update account name |
| POST | /api/auth/change-password | Change password (requires current) |
| POST | /api/auth/delete-account | Delete account (requires password confirmation) |
| POST | /api/auth/forgot-password | Send password reset email |
| POST | /api/auth/reset-password | Reset password with token |
Apps & Keys
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps | List all apps |
| POST | /api/apps | Create app (plan limit enforced) |
| GET | /api/apps/:id | App detail with keys |
| PUT | /api/apps/:id | Update app settings |
| DELETE | /api/apps/:id | Delete app |
| POST | /api/apps/:id/keys | Generate new API key pair |
| DELETE | /api/apps/:id/keys | Revoke an API key |
Billing
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/billing/checkout | Create Stripe checkout session for plan upgrade |
| POST | /api/billing/portal | Open Stripe billing portal for subscription management |
| GET | /api/billing/status | Current billing status and subscription info |
Events & Webhooks
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/history | Query event history (channel, since, until, limit) |
| GET | /api/apps/:id/webhooks | List recent webhook deliveries + status |
| POST | /api/apps/:id/webhooks | Generate or rotate webhook signing secret |
| POST | /api/apps/:id/debug/trigger | Send a test event from the dashboard |
Web Push
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/apps/:id/push/send | Send push notification |
| POST | /api/apps/:id/push/subscribe | Register push subscription |
| GET | /api/apps/:id/push/subscriptions | List subscriptions |
| GET | /api/apps/:id/push/vapid | Get VAPID public key |
| GET | /api/apps/:id/push/history | Push notification delivery history |
Video Rooms
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/video/rooms | List rooms |
| POST | /api/apps/:id/video/rooms | Create room |
| GET | /api/apps/:id/video/rooms/:roomName | Room detail |
| DELETE | /api/apps/:id/video/rooms/:roomName | Delete room |
| GET | /api/apps/:id/video/rooms/:roomName/participants | List participants |
| POST | /api/apps/:id/video/token | Generate participant token |
Messaging
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/messaging/conversations | List conversations |
| POST | /api/apps/:id/messaging/conversations | Create conversation |
| GET | /api/apps/:id/messaging/conversations/:id | Conversation detail |
| GET | /api/apps/:id/messaging/conversations/:id/messages | List messages |
| POST | /api/apps/:id/messaging/conversations/:id/messages | Send message |
| POST | /api/apps/:id/messaging/conversations/:id/participants | Add participant |
| PUT | /api/apps/:id/messaging/conversations/:id/read | Mark as read |
Audio
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/audio/tracks | List tracks |
| POST | /api/apps/:id/audio/tracks | Create track |
| GET | /api/apps/:id/audio/tracks/:trackId | Track detail |
| PUT | /api/apps/:id/audio/tracks/:trackId | Update track |
| DELETE | /api/apps/:id/audio/tracks/:trackId | Delete track |
| GET | /api/apps/:id/audio/tracks/:trackId/play | Get signed playback URL |
| GET | /api/apps/:id/audio/categories | List categories |
Image Processing
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/apps/:id/images/transform | Transform image (resize, convert, crop) |
| POST | /api/apps/:id/images/info | Get image metadata |
| GET | /api/apps/:id/images/stats | Usage statistics |
OCR
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/apps/:id/ocr/extract | Extract text from image |
| GET | /api/apps/:id/ocr/stats | Usage statistics |
Full-Text Search
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/search/indexes | List indexes |
| POST | /api/apps/:id/search/indexes | Create index |
| POST | /api/apps/:id/search/indexes/:name/documents | Add documents |
| POST | /api/apps/:id/search/indexes/:name/search | Search documents |
| DELETE | /api/apps/:id/search/indexes/:name | Delete index |
| GET | /api/apps/:id/search/stats | Usage statistics |
Media Processing
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/media/jobs | List media jobs |
| POST | /api/apps/:id/media/jobs | Create media job |
| GET | /api/apps/:id/media/jobs/:jobId | Job detail + progress |
| POST | /api/apps/:id/media/jobs/:jobId/start | Start processing after upload |
| GET | /api/apps/:id/media/jobs/:jobId/download | Download output file |
| POST | /api/apps/:id/media/probe | Probe file metadata |
| GET | /api/apps/:id/media/stats | Usage statistics |
Workflows
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/workflows | List workflows |
| POST | /api/apps/:id/workflows | Create workflow |
| GET | /api/apps/:id/workflows/:id | Workflow detail |
| PUT | /api/apps/:id/workflows/:id | Update workflow (nodes, edges, config) |
| POST | /api/apps/:id/workflows/:id/run | Execute workflow |
| POST | /api/apps/:id/workflows/:id/status | Change status (DRAFT, ACTIVE, PAUSED, ARCHIVED) |
| POST | /api/apps/:id/workflows/:id/duplicate | Duplicate workflow |
| GET | /api/apps/:id/workflows/:id/runs | List execution history |
React Hooks
The altohost-react package provides first-class React hooks for building real-time UIs with zero boilerplate.
Installation
npm install @girardmedia/altohost-react altohost-jsAltoHostProvider
Wrap your app (or the subtree that needs real-time) with the provider. The connection is created once and shared via context.
import { AltoHostProvider } from '@girardmedia/altohost-react'
export default function Layout({ children }) {
return (
<AltoHostProvider
appKey={process.env.NEXT_PUBLIC_ALTOHOST_KEY}
options={{ wsHost: 'ws.altohost.com' }}
>
{children}
</AltoHostProvider>
)
}useChannel + useEvent
Subscribe to a channel and listen for events with automatic cleanup on unmount.
import { useChannel, useEvent } from '@girardmedia/altohost-react'
export function Notifications() {
const channel = useChannel('alerts')
useEvent(channel, 'new-alert', (data) => {
toast(data.message)
})
return <div>Listening for alerts…</div>
}usePresence
Track who's online with automatic member list updates. Handles the presence- prefix automatically.
import { usePresence } from '@girardmedia/altohost-react'
export function OnlineUsers() {
const { members, me, count } = usePresence('room-1')
return (
<div>
<p>{count} online</p>
{members.map((m) => (
<span key={m.id}>{m.info.name}</span>
))}
</div>
)
}useTrigger
Send client events from the browser. Automatically prepends the client- prefix.
import { useChannel, useTrigger } from '@girardmedia/altohost-react'
export function TypingIndicator() {
const channel = useChannel('chat')
const trigger = useTrigger(channel)
return (
<input
onFocus={() => trigger('typing', { active: true })}
onBlur={() => trigger('typing', { active: false })}
/>
)
}useAltoHost
Access the underlying client instance, connection state, and socket ID for advanced use cases.
import { useAltoHost } from '@girardmedia/altohost-react'
export function ConnectionStatus() {
const { connectionState, socketId } = useAltoHost()
return (
<div>
<span>Status: {connectionState}</span>
{socketId && <span>ID: {socketId}</span>}
</div>
)
}Hook Reference
| Hook | Returns | Purpose |
|---|---|---|
| useAltoHost() | { client, connectionState, socketId } | Access client instance & connection info |
| useChannel(name) | Channel | null | Subscribe to a channel with auto-cleanup |
| useEvent(ch, event, cb) | void | Listen for events with stable callback ref |
| usePresence(name) | { members, me, count, channel } | Real-time presence tracking |
| useTrigger(channel) | (event, data) => void | Send client events from the browser |
| useJobs({ appId }) | { events, getJob, connected } | Real-time background job monitoring |
| useFlags(opts) | { flag(key, default), ready, error, refresh } | Feature flag evaluation with targeting |
| useStream(channel, event, opts?) | { connected, lastMessage, close } | Subscribe to SSE streaming channel |
| useAltoState(docId, init, opts) | { data, update, set, ready, error, peers } | Collaborative CRDT state sync |
Background Jobs
Run background tasks with managed job queues. Create queues, submit jobs, and track status through the dashboard or API. Jobs are isolated per app and gated by plan limits.
Server SDK
import AltoHost from '@girardmedia/altohost-node'
const alto = new AltoHost({
appId: process.env.ALTOHOST_APP_ID,
key: process.env.ALTOHOST_KEY,
secret: process.env.ALTOHOST_SECRET,
host: 'app.altohost.com',
useTLS: true,
dashboardHost: 'https://app.altohost.com',
dashboardApiKey: process.env.ALTOHOST_API_TOKEN,
})
// Submit a job
await alto.jobs.submit('emails', 'send-welcome', { userId: 'u_123' })
// Check job status
const job = await alto.jobs.get('job_abc')
// List jobs in a queue
const jobs = await alto.jobs.list({ queue: 'emails', status: 'completed' })
// Retry a failed job
await alto.jobs.retry('job_abc')REST API
| Method | Endpoint | Description |
|---|---|---|
POST | /api/apps/:id/jobs | Submit a job |
GET | /api/apps/:id/jobs | List jobs (filter by queue, status) |
GET | /api/apps/:id/jobs/:jobId | Get job detail |
POST | /api/apps/:id/jobs/:jobId/retry | Retry a failed job |
POST | /api/apps/:id/jobs/:jobId/cancel | Cancel a job |
Job Event Streaming
Subscribe to real-time job progress updates via WebSocket. When a job transitions status, an event is fired on the private-jobs-{appId} channel.
import AltoHost from '@girardmedia/altohost-js'
const alto = new AltoHost(key, { wsHost: 'ws.altohost.com' })
// Subscribe to job events for your app
const channel = alto.subscribe('private-jobs-YOUR_APP_ID')
channel.on('job.active', (data) => {
console.log('Job started:', data.jobId, data.name)
})
channel.on('job.completed', (data) => {
console.log('Job done:', data.jobId, data.result)
})
channel.on('job.failed', (data) => {
console.error('Job failed:', data.jobId, data.error)
})Events include jobId, name, status, result/error, and timestamps.
Transactional Email
Send transactional emails with template support, variable interpolation, and delivery tracking. Create templates in the dashboard and send from your server.
Server SDK
// Send a raw email
await alto.email.send({
to: 'user@example.com',
subject: 'Welcome to our app',
html: '<h1>Welcome!</h1>',
})
// Send from a template
await alto.email.sendTemplate('welcome', {
to: 'user@example.com',
variables: { name: 'Alice', link: 'https://...' },
})
// Get delivery history
const history = await alto.email.history({ limit: 50 })Redis Cache
Per-app namespaced Redis caching with TTL support. Keys are isolated per app and enforced by plan limits.
Server SDK
// Set a value (TTL in seconds)
await alto.cache.set('user:123', JSON.stringify({ name: 'Alice' }), 3600)
// Get a value
const data = await alto.cache.get('user:123')
// Delete a key
await alto.cache.del('user:123')
// List keys and stats
const keys = await alto.cache.keys('user:*')
const stats = await alto.cache.stats()
// Flush all keys for this app
await alto.cache.flush()SSE Streaming
Server-Sent Events for one-way server-to-client streaming. Ideal for AI token streaming, live feeds, and log tailing. Publish from your server, subscribe from the browser.
Server — Publish events
// Publish an event to a stream channel
await alto.stream.publish('feed', 'update', { message: 'New item' })
// Get active stream stats
const stats = await alto.stream.stats()Client — Subscribe to a stream
import AltoHost from '@girardmedia/altohost-js'
const alto = new AltoHost(key, { wsHost: 'ws.altohost.com' })
// Open an SSE stream
const stream = alto.stream('feed')
stream.on('update', (data) => {
console.log(data.message)
})Web Push
Send browser push notifications via the Web Push protocol. Manage VAPID keys, register subscriptions, target users by tag, and track delivery history. Works with any browser that supports the Push API.
Server SDK
// Get your VAPID public key (for client-side subscription)
const { publicKey} = await alto.push.getVapidKey()
// Register a push subscription from the browser
await alto.push.subscribe({
endpoint: subscription.endpoint,
keys: { p256dh: subscription.keys.p256dh, auth: subscription.keys.auth },
userTag: 'user_123',
})
// Send a push notification to all subscribers
await alto.push.send({
title: 'New Message',
body: 'You have a new notification',
icon: '/icon-192.png',
url: 'https://app.example.com/inbox',
userTag: 'user_123',
})
// List subscriptions and delivery history
const subs = await alto.push.listSubscriptions({ userTag: 'user_123' })
const history = await alto.push.history({ limit: 50 })REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/push/vapid | Get VAPID public key |
POST | /api/apps/:id/push/subscribe | Register subscription |
POST | /api/apps/:id/push/send | Send push notification |
GET | /api/apps/:id/push/subscriptions | List subscriptions (filter by userTag) |
GET | /api/apps/:id/push/history | Delivery history |
Video Rooms
Create and manage video rooms with participant management and token-based access. Generate short-lived tokens with granular permissions for publish, subscribe, and data channels.
Server SDK
// Create a video room
const { room} = await alto.video.createRoom('team-standup', {
maxParticipants: 10,
emptyTimeout: 300,
})
// Generate a participant token
const { token, serverUrl} = await alto.video.createToken({
room: 'team-standup',
identity: 'user_123',
name: 'Alice',
permissions: { canPublish: true, canSubscribe: true },
ttlSeconds: 3600,
})
// List rooms and participants
const { rooms} = await alto.video.listRooms()
const { participants} = await alto.video.listParticipants('team-standup')
// Clean up
await alto.video.deleteRoom('team-standup')REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/video/rooms | List rooms |
POST | /api/apps/:id/video/rooms | Create room |
GET | /api/apps/:id/video/rooms/:roomName | Room detail |
DELETE | /api/apps/:id/video/rooms/:roomName | Delete room |
GET | /api/apps/:id/video/rooms/:roomName/participants | List participants |
POST | /api/apps/:id/video/token | Generate participant token |
Messaging
Build in-app messaging with managed conversations, participants, and message history. Supports direct, group, and support conversation types with role-based participants and read receipts.
Server SDK
// Create a conversation
const convo = await alto.messaging.createConversation({
type: 'GROUP',
title: 'Project Chat',
participants: [
{ externalUserId: 'user_1', displayName: 'Alice', role: 'OWNER' },
{ externalUserId: 'user_2', displayName: 'Bob' },
],
})
// Send a message
await alto.messaging.sendMessage(convo.id, {
senderUserId: 'user_1',
content: 'Hey team, ready for the launch?',
})
// List messages with cursor pagination
const messages = await alto.messaging.listMessages(convo.id, { limit: 25 })
// Mark conversation as read
await alto.messaging.markRead(convo.id, 'user_2')
// Add a participant
await alto.messaging.addParticipant(convo.id, {
externalUserId: 'user_3',
displayName: 'Carol',
role: 'MEMBER',
})REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/messaging/conversations | List conversations |
POST | /api/apps/:id/messaging/conversations | Create conversation |
GET | /api/apps/:id/messaging/conversations/:id | Conversation detail |
POST | /api/apps/:id/messaging/conversations/:id/messages | Send message |
GET | /api/apps/:id/messaging/conversations/:id/messages | List messages |
POST | /api/apps/:id/messaging/conversations/:id/participants | Add participant |
PUT | /api/apps/:id/messaging/conversations/:id/read | Mark as read |
Conversation types: DIRECT, GROUP, SUPPORT. Participant roles: OWNER, ADMIN, MEMBER, OBSERVER.
Audio
Manage audio tracks with metadata, categories, and signed playback URLs. Upload and organize audio content with search, filtering, and expiring playback links.
Server SDK
// Create a track
const track = await alto.audio.createTrack({
title: 'Episode 42 — Launch Day',
artist: 'AltoHost Podcast',
category: 'podcast',
durationMs: 1800000,
})
// List tracks with filtering
const tracks = await alto.audio.listTracks({
category: 'podcast',
published: 'true',
limit: 20,
})
// Get a signed playback URL (expires in 1 hour)
const playback = await alto.audio.getPlayUrl(track.id, 3600)
// Update and delete
await alto.audio.updateTrack(track.id, { isPublished: true })
await alto.audio.deleteTrack(track.id)
// Browse categories
const categories = await alto.audio.listCategories()REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/audio/tracks | List tracks (filter by category, search) |
POST | /api/apps/:id/audio/tracks | Create track |
GET | /api/apps/:id/audio/tracks/:trackId | Track detail |
PUT | /api/apps/:id/audio/tracks/:trackId | Update track |
DELETE | /api/apps/:id/audio/tracks/:trackId | Delete track |
GET | /api/apps/:id/audio/tracks/:trackId/play | Get signed playback URL |
GET | /api/apps/:id/audio/categories | List categories |
Image Processing
Transform images on the fly with resize, crop, format conversion, and filters. Upload an image via multipart form data and receive the processed result. Supports JPEG, PNG, WebP, AVIF, and TIFF.
Server SDK
import fs from 'fs'
const imageBuffer = fs.readFileSync('photo.jpg')
// Resize and convert to WebP
const result = await alto.images.transform(imageBuffer, {
width: 800,
height: 600,
fit: 'cover',
format: 'webp',
quality: 85,
})
// result.buffer — processed image data
// result.width, result.height, result.format, result.size
// Get image metadata without transforming
const info = await alto.images.info(imageBuffer)
// info.width, info.height, info.format, info.hasAlpha
// View usage stats
const stats = await alto.images.stats()Transform Options
| Option | Type | Description |
|---|---|---|
| width / height | number | Output dimensions in pixels |
| fit | string | cover, contain, fill, inside, outside |
| format | string | jpeg, png, webp, avif, tiff |
| quality | number | Output quality (1-100) |
| blur / sharpen | number / boolean | Apply blur (sigma) or sharpen |
| grayscale / rotate / flip / flop | boolean / number | Color and orientation transforms |
REST API
| Method | Endpoint | Description |
|---|---|---|
POST | /api/apps/:id/images/transform | Transform image (multipart form data) |
POST | /api/apps/:id/images/info | Get image metadata |
GET | /api/apps/:id/images/stats | Usage statistics |
OCR
Extract text from images with in-process OCR. Returns full text, confidence scores, and bounding boxes for every word, line, and block. Supports multiple languages.
Server SDK
import fs from 'fs'
const imageBuffer = fs.readFileSync('receipt.png')
// Extract text (default: English)
const result = await alto.ocr.extract(imageBuffer)
console.log(result.text) // full extracted text
console.log(result.confidence) // 0-100 confidence score
console.log(result.wordCount) // total words found
// Extract with specific language
const french = await alto.ocr.extract(imageBuffer, 'fra')
// Access word-level detail
result.words.forEach((w) => {
console.log(w.text, w.confidence, w.bbox)
})
// View usage stats
const stats = await alto.ocr.stats()REST API
| Method | Endpoint | Description |
|---|---|---|
POST | /api/apps/:id/ocr/extract | Extract text from image (multipart form data) |
GET | /api/apps/:id/ocr/stats | Usage statistics |
The response includes words, lines, and blocks arrays, each with text, confidence, and bbox (bounding box coordinates).
Full-Text Search
Add instant full-text search to your app. Create indexes, add documents, and query with filtering, faceting, sorting, and highlighting. Typo-tolerant by default.
Server SDK
// Create an index
await alto.search.createIndex('products', 'id')
// Add documents
await alto.search.addDocuments('products', [
{ id: 1, name: 'Wireless Headphones', price: 79, category: 'audio' },
{ id: 2, name: 'USB-C Hub', price: 45, category: 'accessories' },
{ id: 3, name: 'Mechanical Keyboard', price: 129, category: 'input' },
])
// Search with options
const results = await alto.search.search('products', 'wireless', {
limit: 10,
attributesToHighlight: ['name'],
showRankingScore: true,
})
// results.hits, results.estimatedTotalHits, results.processingTimeMs
// List indexes and stats
const { indexes} = await alto.search.listIndexes()
const stats = await alto.search.stats()
// Delete documents or an entire index
await alto.search.deleteDocuments('products', [1, 2])
await alto.search.deleteIndex('products')REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/search/indexes | List indexes |
POST | /api/apps/:id/search/indexes | Create index |
POST | /api/apps/:id/search/indexes/:name/documents | Add documents |
POST | /api/apps/:id/search/indexes/:name/search | Search documents |
DELETE | /api/apps/:id/search/indexes/:name/documents | Delete documents |
DELETE | /api/apps/:id/search/indexes/:name | Delete index |
GET | /api/apps/:id/search/stats | Usage statistics |
Media Processing
Transcode video, extract audio, generate thumbnails, and probe file metadata. Submit a job, upload via the returned signed URL, then start processing. Download the result when complete.
Server SDK
// Create a transcode job
const { job, uploadUrl} = await alto.media.submit({
type: 'TRANSCODE',
contentType: 'video/mp4',
options: { format: 'webm', videoBitrate: '1M' },
})
// Upload the file to the signed URL
await fetch(uploadUrl, {
method: 'PUT',
body: fileBuffer,
headers: { 'Content-Type': 'video/mp4' },
})
// Start processing after upload
await alto.media.start(job.id)
// Check status
const status = await alto.media.get(job.id)
// List jobs and view stats
const jobs = await alto.media.list({ status: 'COMPLETED', limit: 20 })
const stats = await alto.media.stats()Job Types
| Type | Description |
|---|---|
| TRANSCODE | Convert between video/audio formats |
| THUMBNAIL | Generate thumbnail image from video |
| EXTRACT_AUDIO | Extract audio track from video |
| PROBE | Analyze file metadata (duration, codecs, resolution) |
| CONCATENATE | Join multiple media files |
REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/media/jobs | List media jobs |
POST | /api/apps/:id/media/jobs | Create media job (returns upload URL) |
GET | /api/apps/:id/media/jobs/:jobId | Job detail + progress |
POST | /api/apps/:id/media/jobs/:jobId/start | Start processing after upload |
GET | /api/apps/:id/media/jobs/:jobId/download | Download output file |
POST | /api/apps/:id/media/probe | Probe file metadata |
GET | /api/apps/:id/media/stats | Usage statistics |
Workflows
Build and execute multi-step automation workflows with a visual node editor. Define triggers, conditions, and actions, then run workflows on demand or on a schedule. Track execution history and manage lifecycle states.
Server SDK
// Create a workflow
const workflow = await alto.workflows.create({
name: 'Welcome Sequence',
description: 'Onboarding email drip campaign',
})
// Update workflow definition (nodes, edges, config)
await alto.workflows.update(workflow.id, {
nodes: [
{ id: '1', type: 'trigger', data: { label: 'User Signs Up' } },
{ id: '2', type: 'email', data: { template: 'welcome' } },
],
edges: [{ source: '1', target: '2' }],
})
// Activate the workflow
await alto.workflows.setStatus(workflow.id, 'ACTIVE')
// Run a workflow manually
await alto.workflows.run(workflow.id, { userId: 'u_123' })
// List execution history
const runs = await alto.workflows.listRuns(workflow.id, { limit: 20 })
// Duplicate a workflow
await alto.workflows.duplicate(workflow.id)Lifecycle States
| Status | Description |
|---|---|
| DRAFT | Work in progress, not yet executable by triggers |
| ACTIVE | Live and responding to triggers |
| PAUSED | Temporarily disabled, preserves configuration |
| ARCHIVED | Deactivated and hidden from default views |
REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/workflows | List workflows |
POST | /api/apps/:id/workflows | Create workflow |
GET | /api/apps/:id/workflows/:id | Workflow detail |
PUT | /api/apps/:id/workflows/:id | Update workflow |
DELETE | /api/apps/:id/workflows/:id | Delete workflow |
POST | /api/apps/:id/workflows/:id/run | Execute workflow |
POST | /api/apps/:id/workflows/:id/status | Change lifecycle status |
POST | /api/apps/:id/workflows/:id/duplicate | Duplicate workflow |
GET | /api/apps/:id/workflows/:id/runs | List execution history |
AI Agents
Build autonomous AI agents that use AltoHost platform services as tools. Agents run a ReAct-style reasoning loop, calling tools like email, cache, search, and push notifications to complete tasks. Bring your own OpenAI or Anthropic API key.
Server SDK
// Create an agent
const { agent } = await alto.agents.create({
name: 'Support Bot',
systemPrompt: 'You are a helpful support agent...',
model: 'gpt-4o',
tools: ['send_email', 'cache_get', 'search_query'],
memoryEnabled: true,
})
// Execute the agent
const { run } = await alto.agents.run(agent.id, 'Handle support ticket #1234')
// Check run status
const { run: result } = await alto.agents.getRun(agent.id, run.id)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/agents | List agents with pagination and status filter |
| POST | /api/apps/:id/agents | Create agent with system prompt, model, and tools |
| GET | /api/apps/:id/agents/:agentId | Get agent details |
| PUT | /api/apps/:id/agents/:agentId | Update agent configuration |
| DELETE | /api/apps/:id/agents/:agentId | Delete agent |
| POST | /api/apps/:id/agents/:agentId/run | Execute agent with input text |
| GET | /api/apps/:id/agents/:agentId/runs | List agent runs |
| GET | /api/apps/:id/agents/:agentId/runs/:runId | Get run detail with steps |
| POST | /api/apps/:id/agents/:agentId/runs/:runId/cancel | Cancel running agent |
| GET | /api/apps/:id/agents/:agentId/memory | List agent memories |
| POST | /api/apps/:id/agents/:agentId/memory | Set memory key/value |
| DELETE | /api/apps/:id/agents/:agentId/memory | Delete or clear memories |
| GET | /api/apps/:id/agents/:agentId/tools | List available tools |
Object Storage
S3-compatible object storage with presigned URLs for direct client uploads. Each app gets isolated namespace storage with quota management. Works with any S3-compatible backend.
Server SDK
// 1. Get presigned upload URL
const { url, key } = await alto.storage.upload(
'images/logo.png', 'image/png', 204800
)
// 2. Upload directly to storage (client-side)
await fetch(url, {
method: 'PUT',
body: fileBuffer,
headers: { 'Content-Type': 'image/png' },
})
// 3. Confirm upload
await alto.storage.confirmUpload('images/logo.png', 'image/png', 204800)
// 4. Get download URL
const { downloadUrl } = await alto.storage.download('images/logo.png')
// 5. List objects
const { objects, total } = await alto.storage.list({ prefix: 'images/' })
// 6. Get usage stats
const stats = await alto.storage.stats()REST API
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/apps/:id/storage/upload | Get presigned upload URL |
| PUT | /api/apps/:id/storage/upload | Confirm upload — track object in DB |
| GET | /api/apps/:id/storage/download?path=... | Get presigned download URL |
| GET | /api/apps/:id/storage/objects | List stored objects with filtering |
| DELETE | /api/apps/:id/storage/objects?path=... | Delete object |
| POST | /api/apps/:id/storage/copy | Copy object to new path |
| GET | /api/apps/:id/storage/stats | Storage usage statistics |
Cron Jobs
Per-app scheduled tasks with standard 5-field cron expressions, timezone support, and multiple execution targets. Run HTTP requests, trigger workflows, or fire webhooks on a schedule.
Server SDK
// Create a cron job
const job = await alto.crons.create(appId, {
name: 'Daily Cleanup',
cron: '0 0 * * *',
targetType: 'http',
targetConfig: { url: 'https://api.example.com/cleanup' },
})
// List cron jobs
const { cronJobs, total } = await alto.crons.list(appId)
// Execute manually
const result = await alto.crons.execute(appId, cronJobId)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/crons | List cron jobs |
| POST | /api/apps/:id/crons | Create cron job |
| GET | /api/apps/:id/crons/:cronId | Get cron job details |
| PUT | /api/apps/:id/crons/:cronId | Update cron job |
| DELETE | /api/apps/:id/crons/:cronId | Delete cron job |
| POST | /api/apps/:id/crons/:cronId | Execute cron job or get run history (?action=runs) |
Feature Flags
Built-in feature flag service with rule-based targeting, percentage rollouts, and evaluation analytics. Supports boolean, string, number, and JSON flag types with 14 targeting operators.
Server SDK
// Create a flag
const flag = await alto.flags.create(appId, {
key: 'new-checkout',
name: 'New Checkout Flow',
type: 'boolean',
defaultValue: false,
})
// Evaluate with context
const { value, reason } = await alto.flags.evaluate(appId, {
key: 'new-checkout',
context: { userId: 'user_123', plan: 'pro' },
})
// List all flags
const { flags, total } = await alto.flags.list(appId)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/flags | List feature flags |
| POST | /api/apps/:id/flags | Create feature flag |
| GET | /api/apps/:id/flags/:flagId | Get flag details with evaluation breakdown |
| PUT | /api/apps/:id/flags/:flagId | Update feature flag |
| DELETE | /api/apps/:id/flags/:flagId | Delete feature flag |
| POST | /api/apps/:id/flags/evaluate | Evaluate flag(s) with targeting context |
Webhook Relay
Receive webhooks from any external service, verify signatures, transform payloads, and route to any AltoHost service. Supports Stripe, GitHub, Twilio, Shopify, and custom HMAC verification. Route targets include HTTP, workflow, WebSocket, email, cache, and SSE.
Server SDK
// Create a relay
const relay = await alto.relay.create(appId, {
name: 'Stripe Webhooks',
slug: 'stripe',
routeType: 'workflow',
routeConfig: { workflowId: 'wf_abc123' },
})
// Inbound URL: POST /api/apps/:id/relay/stripe
// List relays
const { relays, total } = await alto.relay.list(appId)
// View event history
const { events } = await alto.relay.events(appId, 'stripe')REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/relay | List webhook relays |
| POST | /api/apps/:id/relay | Create webhook relay |
| GET | /api/apps/:id/relay/:slug | Get relay details or event history (?events=true) |
| PUT | /api/apps/:id/relay/:slug | Update relay configuration |
| DELETE | /api/apps/:id/relay/:slug | Delete relay |
| POST | /api/apps/:id/relay/:slug | Receive incoming webhook (no auth required) |
Usage Alerts
AltoHost monitors your usage against plan limits and sends email alerts when you approach or exceed thresholds. Alerts fire at 80% and 100% of your plan limit with a 24-hour cooldown.
Monitored Metrics
- Messages per day — WebSocket messages processed
- Jobs per day — Background jobs submitted
- Emails per day — Transactional emails sent
- Cache keys — Total keys stored in your app cache
REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/usage/alerts | Alert history + current usage summary |
The dashboard overview tab shows live usage gauges for each metric. Alerts are also visible in the app detail page.
AI Gateway
A unified proxy to OpenAI, Anthropic, Google, Mistral, and Cohere. Route requests through a single API, enable semantic caching to serve identical queries from cache, configure automatic fallback routing across providers, and track cost per request across every model.
Key Features
- Unified API — Single endpoint for all providers with automatic model routing
- Semantic Caching — Cache similar queries with configurable similarity threshold
- Fallback Routing — Automatic failover to backup providers on errors or timeouts
- Cost Tracking — Per-request cost and token tracking with budget alerts
Server SDK
// Send a completion request
const response = await alto.ai.complete(appId, {
model: 'gpt-4o',
messages: [{ role: 'user', content: 'Explain WebSocket channels' }],
cache: true,
fallback: ['claude-sonnet-4-20250514', 'gemini-pro'],
})
// Get usage stats
const stats = await alto.ai.usage(appId, { period: '30d' })REST API
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/apps/:id/ai/complete | Send completion request with caching and fallback |
| GET | /api/apps/:id/ai/usage | Get AI usage stats by model and provider |
| GET | /api/apps/:id/ai/models | List available models and providers |
Prompt Registry
Version-controlled prompt templates with variable interpolation, A/B testing, and instant rollback. Manage prompts as configuration, not code — deploy changes without redeploying your application.
Server SDK
// Create a prompt template
const prompt = await alto.prompts.create(appId, {
name: 'welcome-email',
template: 'Write a welcome email for {{user.name}} who signed up for {{plan}}',
model: 'gpt-4o',
})
// Execute with variables
const result = await alto.prompts.execute(appId, 'welcome-email', {
variables: { user: { name: 'Alice' }, plan: 'Pro' },
})
// List versions
const { versions } = await alto.prompts.versions(appId, 'welcome-email')REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/ai/prompts | List prompt templates |
| POST | /api/apps/:id/ai/prompts | Create prompt template |
| POST | /api/apps/:id/ai/prompts/:name/execute | Execute prompt with variables |
| GET | /api/apps/:id/ai/prompts/:name/versions | List prompt versions |
RAG-as-a-Service
End-to-end retrieval-augmented generation in one API call. Send a query and AltoHost embeds it, searches your vector collections, injects relevant context into the prompt, and returns the LLM response. No orchestration code required.
Server SDK
// RAG query with automatic retrieval
const answer = await alto.ai.rag(appId, {
query: 'How do I set up presence channels?',
collection: 'docs',
model: 'gpt-4o',
topK: 5,
})
// answer.text — LLM response with context
// answer.sources — retrieved documents usedREST API
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/apps/:id/ai/rag | RAG query — embed, search, inject context, return LLM response |
Embedding PipelineBeta
Auto-vectorize realtime-db documents into your vector collections. Configure which fields to embed, select your embedding model, and AltoHost keeps vectors in sync as documents change — no batch jobs required.
Server SDK
// Configure a pipeline
const pipeline = await alto.embeddings.create(appId, {
source: 'docs-collection',
target: 'docs-vectors',
fields: ['title', 'content'],
model: 'text-embedding-3-small',
})
// Check sync status
const status = await alto.embeddings.status(appId, pipeline.id)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/ai/embeddings | List embedding pipelines |
| POST | /api/apps/:id/ai/embeddings | Create embedding pipeline |
| GET | /api/apps/:id/ai/embeddings/:pipelineId | Get pipeline status and sync progress |
AI Observability
Monitor every AI request with token-level, cost-level, and latency dashboards. Break down usage by model, provider, and prompt template. Set budget alerts and inspect full request/response logs from the dashboard.
Key Metrics
- Token usage — Input and output tokens per model and prompt
- Cost tracking — Per-request cost with daily/monthly aggregation
- Latency — P50/P95/P99 latency by model and endpoint
- Cache hit rate — Semantic cache hit/miss ratio and savings
REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/ai/usage | AI usage stats by model and provider |
| GET | /api/apps/:id/ai/logs | Request log with full prompt/response inspection |
Enterprise SSO
Single sign-on with SAML 2.0 and OpenID Connect. Integrate with Okta, Azure Active Directory, Google Workspace, or any standards-compliant identity provider. Enforce SSO-only login and map IdP groups to AltoHost roles.
Supported Providers
- Okta — SAML 2.0 and OIDC with group sync
- Azure AD — Enterprise SAML with conditional access
- Google Workspace — OIDC with domain verification
- Custom SAML/OIDC — Any standards-compliant identity provider
REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/sso | Get SSO configuration |
| PUT | /api/apps/:id/sso | Configure SSO provider |
| POST | /api/apps/:id/sso/test | Test SSO configuration |
Fine-Grained RBAC
Resource-level access control with 30+ permission scopes. Define custom roles, assign them per-app or organization-wide, and audit permission changes. Control exactly which services, apps, and actions each team member can access.
Server SDK
// Create a custom role
const role = await alto.rbac.createRole(appId, {
name: 'developer',
scopes: ['channels:read', 'channels:write', 'jobs:submit', 'cache:read'],
})
// Assign role to a team member
await alto.rbac.assign(appId, { userId: 'user_123', roleId: role.id })
// Check permission
const allowed = await alto.rbac.check(appId, { userId: 'user_123', scope: 'channels:write' })REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/rbac/roles | List custom roles |
| POST | /api/apps/:id/rbac/roles | Create custom role |
| POST | /api/apps/:id/rbac/assign | Assign role to team member |
| POST | /api/apps/:id/rbac/check | Check permission for user |
IP Allowlisting
Restrict API access to specific IP addresses or CIDR ranges on a per-app basis. Requests from non-allowlisted IPs are rejected before reaching your application logic. Supports IPv4 and IPv6 with separate lists for API and dashboard access.
Server SDK
// Add IP ranges
await alto.security.addIpRange(appId, {
cidr: '203.0.113.0/24',
label: 'Office Network',
scope: 'api',
})
// List allowlisted ranges
const { ranges } = await alto.security.listIpRanges(appId)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/security/ip-allowlist | List allowlisted IP ranges |
| POST | /api/apps/:id/security/ip-allowlist | Add IP range to allowlist |
| DELETE | /api/apps/:id/security/ip-allowlist/:rangeId | Remove IP range from allowlist |
Audit Trail Pro
Tamper-evident, hash-chained audit logs for every platform action. Configure retention policies per compliance requirement, export in structured formats for SOC 2, ISO 27001, and HIPAA audits, and search across millions of events with sub-second queries.
Server SDK
// Search audit logs
const { events, total } = await alto.audit.search(appId, {
actor: 'user_123',
action: 'channel.subscribe',
from: '2026-01-01',
to: '2026-03-31',
})
// Export for compliance
const { downloadUrl } = await alto.audit.export(appId, {
format: 'csv',
period: '90d',
})REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/audit | Search audit events with filters |
| POST | /api/apps/:id/audit/export | Export audit logs for compliance |
HIPAA & SOC 2 Compliance
Track HIPAA and SOC 2 posture per app. Acknowledge Business Associate Agreements, attest encryption-at-rest and in-transit, record SOC 2 access review dates, and generate structured compliance reports that aggregate audit trail, api key inventory, team access, and data residency. BUSINESS and ENTERPRISE plans only.
Server SDK
// Read the current compliance profile + plan capabilities
const { profile, capabilities } = await alto.compliance.getProfile()
// Update HIPAA / SOC 2 fields
await alto.compliance.updateProfile({
phiEnabled: true,
retentionDays: 2555,
encryptionAtRestVerified: true,
soc2AccessReviewDate: new Date().toISOString(),
})
// Acknowledge the BAA (auto-enables PHI handling)
await alto.compliance.acknowledgeBaa({
signedBy: 'Dr. Jane Compliance',
contactEmail: 'compliance@hospital.org',
})
// Generate a structured report for the last 90 days
const { report } = await alto.compliance.generateReport(90)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/compliance | Get profile + plan capabilities |
| PUT | /api/apps/:id/compliance | Update HIPAA/SOC 2 fields |
| POST | /api/apps/:id/compliance/baa | Acknowledge BAA (enables PHI) |
| GET | /api/apps/:id/compliance/report?windowDays=90 | Generate structured compliance report |
Data Export
GDPR Article 20 compliant bulk data export in JSON and CSV formats. Schedule periodic exports or trigger on-demand downloads. Covers all service data — messages, jobs, cache entries, files, workflows, and agent runs.
Server SDK
// Start a full export
const job = await alto.dataExport.create(appId, {
format: 'json',
services: ['channels', 'jobs', 'cache', 'storage'],
})
// Check export status
const status = await alto.dataExport.get(appId, job.id)
// Download when complete
const { downloadUrl } = await alto.dataExport.download(appId, job.id)REST API
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/apps/:id/data-export | Start data export job |
| GET | /api/apps/:id/data-export/:jobId | Get export status |
| GET | /api/apps/:id/data-export/:jobId/download | Download export file |
Change Data Capture
Auto-stream database changes to WebSocket channels in real time. Configure CDC on any realtime-db collection and AltoHost broadcasts insert, update, and delete events as they happen. Build live dashboards and sync client state without polling.
Server SDK
// Enable CDC on a collection
const stream = await alto.cdc.create(appId, {
collection: 'orders',
channel: 'orders-feed',
operations: ['insert', 'update', 'delete'],
filter: { status: 'active' },
})
// List active CDC streams
const { streams } = await alto.cdc.list(appId)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/cdc | List CDC streams |
| POST | /api/apps/:id/cdc | Create CDC stream |
| DELETE | /api/apps/:id/cdc/:streamId | Delete CDC stream |
CRDT Collaborative Engine
Real-time collaborative editing with Yjs-backed conflict-free replicated data types. Multiple users can edit the same document simultaneously with automatic conflict resolution, shared undo/redo, and persistent state across sessions.
Server SDK
// Create a collaborative document
const doc = await alto.crdt.create(appId, {
name: 'project-brief',
type: 'text',
})
// Get document state
const state = await alto.crdt.get(appId, doc.id)
// List documents
const { documents } = await alto.crdt.list(appId)React SDK — useAltoState
The useAltoState hook provides reactive collaborative state sync. It loads state from the server, subscribes to real-time updates via WebSocket, and persists changes with debounced writes.
import { useAltoState} from '@girardmedia/altohost-react'
function CollaborativeCounter() {
const { data, update, ready, peers} = useAltoState(
'shared-counter',
{ count: 0 },
{ appId: 'your-app-id' }
)
if (!ready) return <p>Loading...</p>
return (
<div>
<p>Count: {data.count}</p>
<p>{peers} peer(s) connected</p>
<button onClick={() => update({ count: data.count + 1 })}>+1</button>
</div>
)
}Real-Time Channels
CRDT operations automatically broadcast events on private-crdt-{appId}-{documentId} channels. Events: crdt.create, crdt.update, crdt.delete.
REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/crdt | List collaborative documents |
| POST | /api/apps/:id/crdt | Create collaborative document |
| GET | /api/apps/:id/crdt/:docId | Get document state |
| PUT | /api/apps/:id/crdt/:docId | Apply update (base64 binary) |
| DELETE | /api/apps/:id/crdt/:docId | Delete collaborative document |
Event Replay
Never lose events during disconnects. AltoHost stores a configurable window of channel events and replays them when a client reconnects. Specify a timestamp or event ID to resume from, and missed events are delivered in order before switching to live mode.
Client SDK
// Subscribe with replay from last event ID
const channel = alto.subscribe('notifications', {
replay: { fromEventId: 'evt_abc123' },
})
// Subscribe with replay from timestamp
const channel = alto.subscribe('notifications', {
replay: { fromTimestamp: '2026-03-15T10:00:00Z' },
})REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/replay/:channel | Fetch missed events for a channel |
| PUT | /api/apps/:id/replay/config | Configure replay window and retention |
Custom Domains
White-label your WebSocket endpoints by pointing your own domain to AltoHost infrastructure. Serve connections from ws.yourdomain.com with automatic TLS certificate provisioning and renewal. Just add a CNAME record.
Server SDK
// Add a custom domain
const domain = await alto.domains.create(appId, {
domain: 'ws.yourdomain.com',
})
// Check verification status
const status = await alto.domains.verify(appId, domain.id)
// List custom domains
const { domains } = await alto.domains.list(appId)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/domains | List custom domains |
| POST | /api/apps/:id/domains | Add custom domain |
| POST | /api/apps/:id/domains/:domainId/verify | Verify domain DNS configuration |
| DELETE | /api/apps/:id/domains/:domainId | Remove custom domain |
Rate Limiting
Adaptive, ML-powered rate limiting that learns normal traffic patterns per app. Z-score anomaly detection auto-throttles when traffic exceeds 3 standard deviations above the rolling mean. Supports per-endpoint limits, burst allowance, IP reputation scoring, and geo-based rules.
SDK Usage
const rules = await alto.rateLimits.listRules('app-id')
const result = await alto.rateLimits.checkLimit('app-id', {
key: 'user:123',
endpoint: '/api/messages'
})REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/rate-limits | List rate limit rules |
POST | /api/apps/:id/rate-limits | Create rate limit rule |
POST | /api/apps/:id/rate-limits/check | Check rate limit for key |
Predictive Analytics
Time-series forecasting on connection counts, message rates, and API calls. Holt-Winters triple exponential smoothing predicts demand and pre-provisions resources before traffic spikes. Includes trend detection, seasonality analysis, and scaling event recommendations.
SDK Usage
const summary = await alto.analytics.getSummary('app-id')
const forecast = await alto.analytics.forecast('app-id', {
metric: 'connections',
horizon: '7d'
})REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/analytics | Get analytics summary |
POST | /api/apps/:id/analytics | Record analytics event |
POST | /api/apps/:id/analytics/forecast | Get traffic forecast |
Event Mesh
Cross-app pub/sub with permission grants. Apps can subscribe to events from other apps, creating platform network effects. Includes event schema registry (JSON Schema validation), event replay/history, and dead letter routing.
SDK Usage
const topics = await alto.eventMesh.listTopics('app-id')
await alto.eventMesh.publish('app-id', {
topic: 'order.created',
payload: { orderId: 'ord-123' }
})REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/event-mesh | List topics |
POST | /api/apps/:id/event-mesh | Create topic |
POST | /api/apps/:id/event-mesh/publish | Publish event |
POST | /api/apps/:id/event-mesh/subscribe | Subscribe to topic |
Vector Database
Per-app vector storage for embeddings, semantic search, and RAG pipelines. Auto-embed text via BYOK API keys, similarity search with metadata filtering, and hybrid (keyword + vector) search results.
SDK Usage
await alto.vectors.createCollection('app-id', {
name: 'products',
dimension: 1536
})
const results = await alto.vectors.search('app-id', {
collection: 'products',
query: 'wireless headphones',
topK: 10
})REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/vectors/collections | List collections |
POST | /api/apps/:id/vectors/upsert | Upsert vectors |
POST | /api/apps/:id/vectors/search | Semantic search |
Edge Functions
Sandboxed JavaScript/TypeScript functions that run in a dedicated subprocess with vm-level isolation and brokered access to AltoHost platform services. Functions can be created, deployed, executed, and inspected through the dashboard and API. Code size limits, per-app concurrency caps, and frozen context prototypes enforce resource and security boundaries. Production environments should explicitly opt in with EDGE_FUNCTIONS_PREVIEW_ENABLED=true.
SDK Usage
const fn = await alto.functions.create('app-id', {
name: 'on-signup',
code: 'export default async (ctx) => { await ctx.email.send(...) }',
trigger: 'webhook'
})
const result = await alto.functions.execute('app-id', fn.id, { input: { userId: 'u-1' } })REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/functions | List edge functions |
POST | /api/apps/:id/functions | Create function |
GET | /api/apps/:id/functions/:fnId | Get function details |
PUT | /api/apps/:id/functions/:fnId | Update function |
DELETE | /api/apps/:id/functions/:fnId | Delete function |
POST | /api/apps/:id/functions/:fnId | Deploy or execute by passing an action in the request body |
Template Marketplace
Pre-built templates across 5 categories — workflows, functions, agents, cron jobs, and integrations — you can one-click deploy. Browse by category, search by keyword or tag, preview configurations before deploying, and customize after import. 20+ templates included out of the box.
SDK Usage
const list = await alto.templates.list({ category: 'workflow' })
const deployed = await alto.templates.deploy('tmpl-1', 'app-id')REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/templates | List/search templates (filter by category, tags, featured) |
GET | /api/apps/:id/templates/:templateId | Get template detail with full config |
POST | /api/apps/:id/templates/:templateId/deploy | Deploy template to app (creates workflow, function, agent, or cron) |
Managed Queues
FIFO queues, priority queues, dead-letter handling, and fan-out patterns as a first-class API. Supports exactly-once delivery via idempotency keys, message deduplication windows, consumer groups, and per-queue metrics.
SDK Usage
const queue = await alto.queues.create('app-id', {
name: 'tasks',
type: 'fifo'
})
await alto.queues.enqueue('app-id', queue.id, {
body: { task: 'process-payment', amount: 99 }
})REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/queues | List queues |
POST | /api/apps/:id/queues | Create queue |
POST | /api/apps/:id/queues/:queueId/enqueue | Enqueue message |
POST | /api/apps/:id/queues/:queueId/dequeue | Dequeue message |
Realtime Database
Per-app document database with real-time subscriptions — changes sync to all connected clients instantly via WebSocket. Collections, documents, queries with filters/ordering, versioned writes, and a Firebase-compatible SDK API — backed by real PostgreSQL (ACID, joins, indexes).
SDK Usage
await alto.realtimeDb.setDocument('app-id', {
collection: 'users',
documentId: 'user-123',
data: { name: 'Alice', role: 'admin' }
})
const docs = await alto.realtimeDb.query('app-id', {
collection: 'users',
conditions: [{ field: 'role', operator: 'eq', value: 'admin' }]
})REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/realtime-db/collections | List collections |
POST | /api/apps/:id/realtime-db/collections | Create collection |
PUT | /api/apps/:id/realtime-db/documents/:docId | Set document |
POST | /api/apps/:id/realtime-db/query | Query documents |
Presence Plus
Enhanced presence with cursors, selections, typing indicators, and custom state broadcasting. Built on top of the core presence system with additional state management for rich collaborative experiences — Google Docs-style awareness without the complexity.
SDK Usage
await alto.presencePlus.updateState('app-id', {
channel: 'doc-editor',
userId: 'user-1',
state: { cursor: { x: 100, y: 200 }, typing: true }
})
const members = await alto.presencePlus.getChannelState('app-id', 'doc-editor')REST API
| Method | Endpoint | Description |
|---|---|---|
GET | /api/apps/:id/presence-plus/:channel | Get channel presence state |
POST | /api/apps/:id/presence-plus/:channel | Update user state |
DELETE | /api/apps/:id/presence-plus/:channel/:userId | Remove user from channel |
API Versioning
URL-based API versioning with automatic deprecation headers. AltoHost supports multiple active versions simultaneously, provides migration timelines for breaking changes, and adds Sunset and Deprecation headers so clients can prepare for version retirements.
Version Format
- URL-based —
/api/v1/apps/:id/...and/api/v2/apps/:id/... - Deprecation headers — Responses include Sunset date and migration guide URL
- Per-version analytics — Track which clients still use deprecated versions
OpenTelemetry
Instrument your AltoHost services and export traces, metrics, and logs to any OpenTelemetry-compatible collector. AltoHost auto-instruments all API calls, WebSocket connections, job executions, and workflow runs with distributed trace context.
Server SDK
// Configure OpenTelemetry export
await alto.otel.configure(appId, {
endpoint: 'https://otel.yourdomain.com:4318',
protocol: 'otlp-http',
headers: { Authorization: 'Bearer your-token' },
})
// Check export status
const config = await alto.otel.get(appId)REST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/apps/:id/otel | Get OpenTelemetry configuration |
| PUT | /api/apps/:id/otel | Configure OTLP export endpoint |
GraphQL Gateway
Auto-generated GraphQL schema from your REST API endpoints. AltoHost introspects your app configuration and creates queries, mutations, and subscriptions. Use a single GraphQL endpoint with built-in DataLoader batching to prevent N+1 queries.
Usage
// GraphQL endpoint for your app
// POST /api/apps/:id/graphql
// Example query
const response = await fetch('/api/apps/123/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: '{ channels { name occupied subscriberCount } }',
})
})REST API
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/apps/:id/graphql | Execute GraphQL query or mutation |
| GET | /api/apps/:id/graphql/schema | Introspect auto-generated schema |
White-Label Reselling
Resell the platform under your own brand. BUSINESS and ENTERPRISE plans can create a reseller profile with custom branding (logo, colors, support email, custom domain), then manage sub-accounts (clients) whose dashboards display the reseller's branding instead of the default platform brand.Reseller Profile
Create and manage your reseller profile with branding configuration. Clients managed by a reseller automatically see the reseller's branding when they log in to their dashboard.// Create reseller profile
const { ResellerClient } = require('@girardmedia/altohost-node')
const reseller = new ResellerClient('https://app.altohost.com', 'alto_token_xxx')
// Set up your brand
await reseller.createProfile({
brandName: 'MyPlatform',
primaryColor: '#8b5cf6',
supportEmail: 'support@myplatform.com',
customDomain: 'platform.myplatform.com',
})
// Add a client (creates or links account)
await reseller.addClient({
email: 'client@example.com',
name: 'Acme Corp',
plan: 'PRO',
})
// List clients
const { clients, total } = await reseller.listClients({ search: 'acme' })
// Update client plan
await reseller.updateClient(clientId, { plan: 'BUSINESS' })
// Resolve branding for any account
const { branding } = await reseller.getBranding()
// Returns reseller branding if account is managed, null otherwiseREST API
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/reseller | Get reseller profile + stats |
| POST | /api/reseller | Create reseller profile |
| PUT | /api/reseller | Update reseller profile |
| DELETE | /api/reseller | Delete reseller profile |
| GET | /api/reseller/clients | List managed clients |
| POST | /api/reseller/clients | Add client account |
| GET | /api/reseller/clients/:id | Get client detail |
| PUT | /api/reseller/clients/:id | Update client plan/notes |
| DELETE | /api/reseller/clients/:id | Remove client |
| GET | /api/reseller/branding | Resolve branding for current user |
System Notifications
AltoHost automatically sends email notifications for important account, security, and billing events.
| Trigger | Description | |
|---|---|---|
| Email Verification | Account registration | Confirms email address with verification link |
| Welcome | Email verified | Onboarding with getting-started steps |
| Password Reset | Forgot password request | Link to set a new password (expires in 1 hour) |
| Password Reset Confirmation | Password reset completed | Confirms the reset and session revocation |
| Password Changed | Password change via settings | Security confirmation of password change |
| 2FA Enabled | Two-factor authentication enabled | Confirms 2FA activation |
| 2FA Disabled | Two-factor authentication disabled | Security alert for 2FA removal |
| Login Alert | Successful sign-in | Shows IP, device, and timestamp |
| Team Invite | Member added to team | Notifies the invited user with role info |
| Account Deleted | Account deletion | Confirms permanent deletion |
| Plan Change | Subscription upgrade/downgrade | Confirms plan transition |
| Subscription Cancelled | Subscription deleted | Notifies downgrade to FREE plan |
| Payment Failed | Invoice payment failed | Urgent alert with update-payment CTA |
| Payment Receipt | Invoice paid | Receipt with amount and invoice link |
| Usage Alert (80%) | Approaching plan limit | Warning with current usage metrics |
| Usage Alert (100%) | Plan limit reached | Alert to upgrade plan |
All system emails are sent via Resend. Configure RESEND_API_KEY and EMAIL_FROM environment variables. Emails use a dark-themed responsive HTML layout.
Migrating from Pusher
AltoHost is designed as a drop-in replacement for Pusher. Most applications can migrate in under 10 minutes with minimal code changes.
What stays the same
- Channel naming conventions (
public,private-,presence-) - Authentication flow (server-side auth endpoint)
- Webhook events and structure
- Event data format (JSON)
What changes
| Pusher | AltoHost | Notes |
|---|---|---|
| new Pusher(key, { cluster }) | new AltoHost(key, { wsHost }) | Key is first arg, options second |
| pusher.subscribe('ch') | alto.subscribe('ch') | Identical API |
| channel.bind('event', cb) | channel.on('event', cb) | bind → on |
| channel.unbind('event', cb) | channel.off('event', cb) | unbind → off |
| Pusher.trigger(ch, ev, data) | alto.trigger(ch, ev, data) | Same server API |
| PUSHER_APP_ID | ALTOHOST_APP_ID | Rename env vars |
| PUSHER_KEY | ALTOHOST_KEY | |
| PUSHER_SECRET | ALTOHOST_SECRET | |
| PUSHER_CLUSTER | Not needed | Uses host instead |
Step-by-step migration
- 1Install AltoHost SDKs:
npm install @girardmedia/altohost-js altohost-node - 2Update environment variables (rename
PUSHER_*toALTOHOST_*) - 3Update server imports:
Pusher→AltoHostfrom'@girardmedia/altohost-node' - 4Update client imports:
Pusher→AltoHostfrom'@girardmedia/altohost-js' - 5Replace
.bind()with.on()and.unbind()with.off() - 6Update auth endpoint to use AltoHost SDK
- 7Remove Pusher packages:
npm uninstall pusher pusher-js
Chat app migration example
A complete before-and-after showing how a chat app migrates from Pusher to AltoHost.
Client — Before (Pusher)
import Pusher from 'pusher-js'
const pusher = new Pusher('app-key', { cluster: 'us1' })
const channel = pusher.subscribe('chat-room')
channel.bind('new-message', (data) => {
addMessage(data)
})Client — After (AltoHost)
import AltoHost from '@girardmedia/altohost-js'
const alto = new AltoHost('app-key', { wsHost: 'ws.altohost.com' })
const channel = alto.subscribe('chat-room')
channel.on('new-message', (data) => {
addMessage(data)
})Server — Before (Pusher)
import Pusher from 'pusher'
const pusher = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.PUSHER_KEY,
secret: process.env.PUSHER_SECRET,
cluster: process.env.PUSHER_CLUSTER,
})
await pusher.trigger('chat-room', 'new-message', { text, user })Server — After (AltoHost)
import { AltoHost } from '@girardmedia/altohost-node'
const alto = new AltoHost({
appId: process.env.ALTOHOST_APP_ID,
key: process.env.ALTOHOST_KEY,
secret: process.env.ALTOHOST_SECRET,
host: process.env.ALTOHOST_HOST,
})
await alto.trigger('chat-room', 'new-message', { text, user })Ready to get started?
Create a free account and start sending real-time events in minutes.