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 |
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.
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.