Documentation

Everything you need to integrate real-time features with AltoHost.

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:

.envCopy
ALTOHOST_APP_ID=your-app-id
ALTOHOST_KEY=your-app-key
ALTOHOST_SECRET=your-app-secret
ALTOHOST_HOST=ws.altohost.com
ALTOHOST_PORT=443

3. Install SDKs

TerminalCopy
# Server
npm install @girardmedia/altohost-node

# Client
npm install @girardmedia/altohost-js

4. Send your first event

Trigger an event from the server and listen for it on the client.

server.tsCopy
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'
})
client.tsCopy
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.

VariableDescriptionRequired
ALTOHOST_APP_IDYour application ID (found in dashboard)Required
ALTOHOST_KEYYour API keyRequired
ALTOHOST_SECRETYour API secret (server-side only, never expose to client)Required (server)
ALTOHOST_HOSTWebSocket server host (ws.altohost.com)Optional
ALTOHOST_PORTWebSocket server port (443)Optional
Security warning: Never expose your 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

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

trigger.tsCopy
// 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.

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

auth.tsCopy
// 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.

channel-info.tsCopy
const info = await alto.getChannelInfo('presence-lobby')
console.log(info.occupied)       // true
console.log(info.user_count)    // 5
console.log(info.subscription_count) // 7

alto.getPresenceMembers(channel)

Get the list of users currently subscribed to a presence channel.

presence-members.tsCopy
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

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

subscribe.tsCopy
const channel = alto.subscribe('news')

channel.on(event, callback)

Listen for events on a channel. Also available as .bind() for convenience.

listen.tsCopy
channel.on('breaking', (data) => {
  console.log(data.headline)
})

channel.off(event, callback)

Remove an event listener. Also available as .unbind() for convenience.

off.tsCopy
// Remove a specific handler
channel.off('breaking', myHandler)

// Remove all handlers for an event
channel.off('breaking')

alto.unsubscribe(channel)

Unsubscribe from a channel.

unsubscribe.tsCopy
alto.unsubscribe('news')

alto.disconnect()

Disconnect from the WebSocket server and clean up all subscriptions.

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

public-channel.tsCopy
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.

private-channel.tsCopy
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.

presence-channel.tsCopy
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.

app/api/altohost/auth/route.tsCopy
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.

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

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

typing-indicator.tsCopy
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)
})
Note: Client events must be enabled for your app in the dashboard settings. They are available on Starter plans and above.

Webhooks

Configure a webhook URL in your app settings to receive real-time notifications about channel and presence events on your server.

Webhook events

EventDescription
channel_occupiedFirst subscriber joins a channel
channel_vacatedLast subscriber leaves a channel
member_addedUser joins a presence channel
member_removedUser 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.

webhook-payload.jsonCopy
{
  "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.

app/api/webhooks/altohost/route.tsCopy
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

HeaderDescription
X-AltoHost-SignatureHMAC-SHA256 hex digest of the raw request body
X-AltoHost-DeliveryUnique delivery ID (for deduplication)
X-AltoHost-AttemptDelivery 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:

curlCopy
curl -H "Authorization: Bearer alto_your_token_here" \\
  https://app.altohost.com/api/apps

Token Scopes

ScopePermissions
apps:readList and view apps, keys, channels, usage
apps:writeCreate, update, and delete apps plus non-secret app settings
events:triggerTrigger events and use debug tools
secrets:manageView app secrets, rotate webhook secrets, and manage app key credentials
tokens:manageCreate and revoke API tokens
Security: Tokens are hashed before storage and shown only once. Treat them like passwords, use the minimum scopes possible, and reserve 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

TerminalCopy
# 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

ParameterTypeDescription
channelstringFilter by channel name
eventstringFilter by event type (CONNECT, SUBSCRIBE, CLIENT_EVENT, etc.)
sinceISO 8601Return events after this timestamp
untilISO 8601Return events before this timestamp
limitnumberMax events to return (default 100, max 500)

Response format

response.jsonCopy
{
  "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

MethodEndpointDescription
POST/api/auth/registerCreate account (name, email, password)
POST/api/auth/loginLogin, sets session cookie
POST/api/auth/logoutDestroy session
GET/api/auth/meCurrent account info
PUT/api/auth/update-profileUpdate account name
POST/api/auth/change-passwordChange password (requires current)
POST/api/auth/delete-accountDelete account (requires password confirmation)
POST/api/auth/forgot-passwordSend password reset email
POST/api/auth/reset-passwordReset password with token

Apps & Keys

MethodEndpointDescription
GET/api/appsList all apps
POST/api/appsCreate app (plan limit enforced)
GET/api/apps/:idApp detail with keys
PUT/api/apps/:idUpdate app settings
DELETE/api/apps/:idDelete app
POST/api/apps/:id/keysGenerate new API key pair
DELETE/api/apps/:id/keysRevoke an API key

Billing

MethodEndpointDescription
POST/api/billing/checkoutCreate Stripe checkout session for plan upgrade
POST/api/billing/portalOpen Stripe billing portal for subscription management
GET/api/billing/statusCurrent billing status and subscription info

Events & Webhooks

MethodEndpointDescription
GET/api/apps/:id/historyQuery event history (channel, since, until, limit)
GET/api/apps/:id/webhooksList recent webhook deliveries + status
POST/api/apps/:id/webhooksGenerate or rotate webhook signing secret
POST/api/apps/:id/debug/triggerSend a test event from the dashboard

Web Push

MethodEndpointDescription
POST/api/apps/:id/push/sendSend push notification
POST/api/apps/:id/push/subscribeRegister push subscription
GET/api/apps/:id/push/subscriptionsList subscriptions
GET/api/apps/:id/push/vapidGet VAPID public key
GET/api/apps/:id/push/historyPush notification delivery history

Video Rooms

MethodEndpointDescription
GET/api/apps/:id/video/roomsList rooms
POST/api/apps/:id/video/roomsCreate room
GET/api/apps/:id/video/rooms/:roomNameRoom detail
DELETE/api/apps/:id/video/rooms/:roomNameDelete room
GET/api/apps/:id/video/rooms/:roomName/participantsList participants
POST/api/apps/:id/video/tokenGenerate participant token

Messaging

MethodEndpointDescription
GET/api/apps/:id/messaging/conversationsList conversations
POST/api/apps/:id/messaging/conversationsCreate conversation
GET/api/apps/:id/messaging/conversations/:idConversation detail
GET/api/apps/:id/messaging/conversations/:id/messagesList messages
POST/api/apps/:id/messaging/conversations/:id/messagesSend message
POST/api/apps/:id/messaging/conversations/:id/participantsAdd participant
PUT/api/apps/:id/messaging/conversations/:id/readMark as read

Audio

MethodEndpointDescription
GET/api/apps/:id/audio/tracksList tracks
POST/api/apps/:id/audio/tracksCreate track
GET/api/apps/:id/audio/tracks/:trackIdTrack detail
PUT/api/apps/:id/audio/tracks/:trackIdUpdate track
DELETE/api/apps/:id/audio/tracks/:trackIdDelete track
GET/api/apps/:id/audio/tracks/:trackId/playGet signed playback URL
GET/api/apps/:id/audio/categoriesList categories

Image Processing

MethodEndpointDescription
POST/api/apps/:id/images/transformTransform image (resize, convert, crop)
POST/api/apps/:id/images/infoGet image metadata
GET/api/apps/:id/images/statsUsage statistics

OCR

MethodEndpointDescription
POST/api/apps/:id/ocr/extractExtract text from image
GET/api/apps/:id/ocr/statsUsage statistics

Full-Text Search

MethodEndpointDescription
GET/api/apps/:id/search/indexesList indexes
POST/api/apps/:id/search/indexesCreate index
POST/api/apps/:id/search/indexes/:name/documentsAdd documents
POST/api/apps/:id/search/indexes/:name/searchSearch documents
DELETE/api/apps/:id/search/indexes/:nameDelete index
GET/api/apps/:id/search/statsUsage statistics

Media Processing

MethodEndpointDescription
GET/api/apps/:id/media/jobsList media jobs
POST/api/apps/:id/media/jobsCreate media job
GET/api/apps/:id/media/jobs/:jobIdJob detail + progress
POST/api/apps/:id/media/jobs/:jobId/startStart processing after upload
GET/api/apps/:id/media/jobs/:jobId/downloadDownload output file
POST/api/apps/:id/media/probeProbe file metadata
GET/api/apps/:id/media/statsUsage statistics

Workflows

MethodEndpointDescription
GET/api/apps/:id/workflowsList workflows
POST/api/apps/:id/workflowsCreate workflow
GET/api/apps/:id/workflows/:idWorkflow detail
PUT/api/apps/:id/workflows/:idUpdate workflow (nodes, edges, config)
POST/api/apps/:id/workflows/:id/runExecute workflow
POST/api/apps/:id/workflows/:id/statusChange status (DRAFT, ACTIVE, PAUSED, ARCHIVED)
POST/api/apps/:id/workflows/:id/duplicateDuplicate workflow
GET/api/apps/:id/workflows/:id/runsList execution history

React Hooks

The altohost-react package provides first-class React hooks for building real-time UIs with zero boilerplate.

Installation

TerminalCopy
npm install @girardmedia/altohost-react altohost-js

AltoHostProvider

Wrap your app (or the subtree that needs real-time) with the provider. The connection is created once and shared via context.

app/layout.tsxCopy
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.

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

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

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

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

HookReturnsPurpose
useAltoHost(){ client, connectionState, socketId }Access client instance & connection info
useChannel(name)Channel | nullSubscribe to a channel with auto-cleanup
useEvent(ch, event, cb)voidListen for events with stable callback ref
usePresence(name){ members, me, count, channel }Real-time presence tracking
useTrigger(channel)(event, data) => voidSend 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

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

MethodEndpointDescription
POST/api/apps/:id/jobsSubmit a job
GET/api/apps/:id/jobsList jobs (filter by queue, status)
GET/api/apps/:id/jobs/:jobIdGet job detail
POST/api/apps/:id/jobs/:jobId/retryRetry a failed job
POST/api/apps/:id/jobs/:jobId/cancelCancel 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.

job-streaming.tsCopy
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

email.tsCopy
// 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

cache.tsCopy
// 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

sse-publish.tsCopy
// 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

sse-client.tsCopy
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

push.tsCopy
// 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

MethodEndpointDescription
GET/api/apps/:id/push/vapidGet VAPID public key
POST/api/apps/:id/push/subscribeRegister subscription
POST/api/apps/:id/push/sendSend push notification
GET/api/apps/:id/push/subscriptionsList subscriptions (filter by userTag)
GET/api/apps/:id/push/historyDelivery 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

video.tsCopy
// 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

MethodEndpointDescription
GET/api/apps/:id/video/roomsList rooms
POST/api/apps/:id/video/roomsCreate room
GET/api/apps/:id/video/rooms/:roomNameRoom detail
DELETE/api/apps/:id/video/rooms/:roomNameDelete room
GET/api/apps/:id/video/rooms/:roomName/participantsList participants
POST/api/apps/:id/video/tokenGenerate 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

messaging.tsCopy
// 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

MethodEndpointDescription
GET/api/apps/:id/messaging/conversationsList conversations
POST/api/apps/:id/messaging/conversationsCreate conversation
GET/api/apps/:id/messaging/conversations/:idConversation detail
POST/api/apps/:id/messaging/conversations/:id/messagesSend message
GET/api/apps/:id/messaging/conversations/:id/messagesList messages
POST/api/apps/:id/messaging/conversations/:id/participantsAdd participant
PUT/api/apps/:id/messaging/conversations/:id/readMark 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

audio.tsCopy
// 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

MethodEndpointDescription
GET/api/apps/:id/audio/tracksList tracks (filter by category, search)
POST/api/apps/:id/audio/tracksCreate track
GET/api/apps/:id/audio/tracks/:trackIdTrack detail
PUT/api/apps/:id/audio/tracks/:trackIdUpdate track
DELETE/api/apps/:id/audio/tracks/:trackIdDelete track
GET/api/apps/:id/audio/tracks/:trackId/playGet signed playback URL
GET/api/apps/:id/audio/categoriesList 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

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

OptionTypeDescription
width / heightnumberOutput dimensions in pixels
fitstringcover, contain, fill, inside, outside
formatstringjpeg, png, webp, avif, tiff
qualitynumberOutput quality (1-100)
blur / sharpennumber / booleanApply blur (sigma) or sharpen
grayscale / rotate / flip / flopboolean / numberColor and orientation transforms

REST API

MethodEndpointDescription
POST/api/apps/:id/images/transformTransform image (multipart form data)
POST/api/apps/:id/images/infoGet image metadata
GET/api/apps/:id/images/statsUsage 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

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

MethodEndpointDescription
POST/api/apps/:id/ocr/extractExtract text from image (multipart form data)
GET/api/apps/:id/ocr/statsUsage statistics

The response includes words, lines, and blocks arrays, each with text, confidence, and bbox (bounding box coordinates).

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

media.tsCopy
// 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

TypeDescription
TRANSCODEConvert between video/audio formats
THUMBNAILGenerate thumbnail image from video
EXTRACT_AUDIOExtract audio track from video
PROBEAnalyze file metadata (duration, codecs, resolution)
CONCATENATEJoin multiple media files

REST API

MethodEndpointDescription
GET/api/apps/:id/media/jobsList media jobs
POST/api/apps/:id/media/jobsCreate media job (returns upload URL)
GET/api/apps/:id/media/jobs/:jobIdJob detail + progress
POST/api/apps/:id/media/jobs/:jobId/startStart processing after upload
GET/api/apps/:id/media/jobs/:jobId/downloadDownload output file
POST/api/apps/:id/media/probeProbe file metadata
GET/api/apps/:id/media/statsUsage 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

workflows.tsCopy
// 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

StatusDescription
DRAFTWork in progress, not yet executable by triggers
ACTIVELive and responding to triggers
PAUSEDTemporarily disabled, preserves configuration
ARCHIVEDDeactivated and hidden from default views

REST API

MethodEndpointDescription
GET/api/apps/:id/workflowsList workflows
POST/api/apps/:id/workflowsCreate workflow
GET/api/apps/:id/workflows/:idWorkflow detail
PUT/api/apps/:id/workflows/:idUpdate workflow
DELETE/api/apps/:id/workflows/:idDelete workflow
POST/api/apps/:id/workflows/:id/runExecute workflow
POST/api/apps/:id/workflows/:id/statusChange lifecycle status
POST/api/apps/:id/workflows/:id/duplicateDuplicate workflow
GET/api/apps/:id/workflows/:id/runsList 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

agents.tsCopy
// 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

MethodEndpointDescription
GET/api/apps/:id/agentsList agents with pagination and status filter
POST/api/apps/:id/agentsCreate agent with system prompt, model, and tools
GET/api/apps/:id/agents/:agentIdGet agent details
PUT/api/apps/:id/agents/:agentIdUpdate agent configuration
DELETE/api/apps/:id/agents/:agentIdDelete agent
POST/api/apps/:id/agents/:agentId/runExecute agent with input text
GET/api/apps/:id/agents/:agentId/runsList agent runs
GET/api/apps/:id/agents/:agentId/runs/:runIdGet run detail with steps
POST/api/apps/:id/agents/:agentId/runs/:runId/cancelCancel running agent
GET/api/apps/:id/agents/:agentId/memoryList agent memories
POST/api/apps/:id/agents/:agentId/memorySet memory key/value
DELETE/api/apps/:id/agents/:agentId/memoryDelete or clear memories
GET/api/apps/:id/agents/:agentId/toolsList 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

storage.tsCopy
// 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

MethodEndpointDescription
POST/api/apps/:id/storage/uploadGet presigned upload URL
PUT/api/apps/:id/storage/uploadConfirm upload — track object in DB
GET/api/apps/:id/storage/download?path=...Get presigned download URL
GET/api/apps/:id/storage/objectsList stored objects with filtering
DELETE/api/apps/:id/storage/objects?path=...Delete object
POST/api/apps/:id/storage/copyCopy object to new path
GET/api/apps/:id/storage/statsStorage 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

cron.tsCopy
// 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

MethodEndpointDescription
GET/api/apps/:id/cronsList cron jobs
POST/api/apps/:id/cronsCreate cron job
GET/api/apps/:id/crons/:cronIdGet cron job details
PUT/api/apps/:id/crons/:cronIdUpdate cron job
DELETE/api/apps/:id/crons/:cronIdDelete cron job
POST/api/apps/:id/crons/:cronIdExecute 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

flags.tsCopy
// 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

MethodEndpointDescription
GET/api/apps/:id/flagsList feature flags
POST/api/apps/:id/flagsCreate feature flag
GET/api/apps/:id/flags/:flagIdGet flag details with evaluation breakdown
PUT/api/apps/:id/flags/:flagIdUpdate feature flag
DELETE/api/apps/:id/flags/:flagIdDelete feature flag
POST/api/apps/:id/flags/evaluateEvaluate 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

relay.tsCopy
// 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

MethodEndpointDescription
GET/api/apps/:id/relayList webhook relays
POST/api/apps/:id/relayCreate webhook relay
GET/api/apps/:id/relay/:slugGet relay details or event history (?events=true)
PUT/api/apps/:id/relay/:slugUpdate relay configuration
DELETE/api/apps/:id/relay/:slugDelete relay
POST/api/apps/:id/relay/:slugReceive 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

MethodEndpointDescription
GET/api/apps/:id/usage/alertsAlert 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

PusherAltoHostNotes
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_IDALTOHOST_APP_IDRename env vars
PUSHER_KEYALTOHOST_KEY
PUSHER_SECRETALTOHOST_SECRET
PUSHER_CLUSTERNot neededUses host instead

Step-by-step migration

  1. 1Install AltoHost SDKs: npm install @girardmedia/altohost-js altohost-node
  2. 2Update environment variables (rename PUSHER_* to ALTOHOST_*)
  3. 3Update server imports: PusherAltoHost from '@girardmedia/altohost-node'
  4. 4Update client imports: PusherAltoHost from '@girardmedia/altohost-js'
  5. 5Replace .bind() with .on() and .unbind() with .off()
  6. 6Update auth endpoint to use AltoHost SDK
  7. 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)

client.ts — PusherCopy
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)

client.ts — AltoHostCopy
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)

server.ts — PusherCopy
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)

server.ts — AltoHostCopy
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.