@orbitconnect/server
v0.1.2
Published
OrbitConnect Server SDK — for use in developer backends
Maintainers
Readme
@orbitconnect/server
Node.js server SDK for OrbitConnect. Manage users, conversations, calls, meetings, media, webhooks, and billing from your backend.
All requests go to
https://orbitxyz.muvle.org/api/v1. This base URL is fixed and cannot be overridden.
Install
npm install @orbitconnect/serverQuick start
import { OrbitServer } from '@orbitconnect/server'
const orbit = new OrbitServer({ secretKey: process.env.ORBIT_SECRET_KEY! })
// Register a user
const user = await orbit.users.create({
external_id: 'your_db_user_id',
display_name: 'Alice',
})
// Issue a short-lived token for the React SDK
const { token, expires_at } = await orbit.users.createToken(user.id)
// Pass token + clientId to your frontend
res.json({ token, expires_at, clientId: process.env.ORBIT_CLIENT_ID })Constructor
new OrbitServer({ secretKey: string })| Option | Type | Required | Description |
| --- | --- | --- | --- |
| secretKey | string | ✓ | Your secret API key (sk_live_… or sk_test_…) |
Never expose your secret key in client-side code.
Namespaces
| Namespace | Description |
| --- | --- |
| orbit.users | Create and manage app users, issue short-lived tokens |
| orbit.conversations | Direct and group conversations, participant management |
| orbit.messages | Send, list, search, and sync messages |
| orbit.calls | Voice/video call sessions, WebRTC signaling, recordings |
| orbit.meetings | Meeting rooms, participants, screen share, recordings |
| orbit.media | Upload, transcode, stream, and access-control media files |
| orbit.realtime | Session orchestration — escalate chat → call → meeting |
| orbit.webhooks | Register endpoints, subscribe to events, replay deliveries |
| orbit.billing | Wallet, usage events, invoices, and low-balance alerts |
orbit.users
// Create or upsert (idempotent on external_id)
const user = await orbit.users.create({ external_id, display_name, metadata? })
// List / get
const users = await orbit.users.list(activeOnly?)
const user = await orbit.users.get(id)
const user = await orbit.users.getByExternalId(externalId)
// Update / deactivate
await orbit.users.update(id, { display_name?, metadata? })
await orbit.users.deactivate(id) // soft-delete, preserves history
// Token issuance
const { token, expires_at } = await orbit.users.createToken(userId, { ttl?: number })
// ttl in seconds — default 3600, max 86400orbit.conversations
// Direct conversation
const conv = await orbit.conversations.create(
{ type: 'direct', participant_id: userBId },
actingUserId
)
// Group conversation
const group = await orbit.conversations.create(
{ type: 'group', participant_ids: [a, b, c], metadata: { name: 'Team Alpha' } },
actingUserId
)
await orbit.conversations.list(actingUserId)
await orbit.conversations.get(convId, actingUserId)
await orbit.conversations.update(convId, data, actingUserId)
// Participants
await orbit.conversations.listParticipants(convId, actingUserId)
await orbit.conversations.addParticipant(convId, userId, actingUserId)
await orbit.conversations.removeParticipant(convId, userId, actingUserId)orbit.messages
await orbit.messages.send({ conversation_id, content, type?, metadata? }, actingUserId)
await orbit.messages.list(convId, { limit?, offset? }, actingUserId)
await orbit.messages.get(messageId, actingUserId)
await orbit.messages.delete(messageId, actingUserId)
await orbit.messages.search(convId, query, actingUserId, limit?)
await orbit.messages.sync(convId, sinceDate, actingUserId, limit?)orbit.calls
// Lifecycle
const session = await orbit.calls.initiate({ callee_id, type: 'video' }, callerId)
await orbit.calls.accept(callId, calleeId)
await orbit.calls.reject(callId, calleeId)
await orbit.calls.end(callId, actingUserId)
// WebRTC (server-side signaling helpers)
const token = await orbit.calls.getWebrtcToken(actingUserId)
const turn = await orbit.calls.getTurnCredentials(actingUserId)
await orbit.calls.submitOffer(callId, sdp, actingUserId)
await orbit.calls.submitAnswer(callId, sdp, actingUserId)
await orbit.calls.addIceCandidate(callId, { candidate }, actingUserId)
// Quality & recordings
await orbit.calls.submitMetrics(callId, { bitrate, packet_loss, jitter, rtt }, actingUserId)
await orbit.calls.getMetrics(callId, actingUserId)
await orbit.calls.toggleRecord(callId, actingUserId)
await orbit.calls.listRecordings(callId, actingUserId)WebRTC offer/answer/ICE exchange is handled automatically by the React SDK's
VideoCallwidget anduseCall()hook. You only need the server-side helpers for custom signaling flows.
orbit.meetings
// Lifecycle
const meeting = await orbit.meetings.create({ title, description?, scheduled_at? }, hostId)
await orbit.meetings.start(meetingId, hostId)
const { meeting, sfu_session } = await orbit.meetings.join(meetingId, userId, token?)
await orbit.meetings.leave(meetingId, userId)
await orbit.meetings.end(meetingId, hostId)
// Participants
await orbit.meetings.addParticipant(meetingId, userId, actingUserId)
await orbit.meetings.removeParticipant(meetingId, userId, actingUserId)
await orbit.meetings.kickParticipant(meetingId, userId, actingUserId)
await orbit.meetings.updateRole(meetingId, userId, role, actingUserId)
await orbit.meetings.mute(meetingId, userId, actingUserId)
await orbit.meetings.unmute(meetingId, userId, actingUserId)
// Tokens & recordings
const token = await orbit.meetings.generateToken(meetingId, { app_user_id?, role? }, actingUserId)
await orbit.meetings.startRecording(meetingId, actingUserId)
await orbit.meetings.stopRecording(meetingId, actingUserId)
await orbit.meetings.listRecordings(meetingId, actingUserId)orbit.media
// Upload
const session = await orbit.media.generateUploadUrl(mimeType, actingUserId)
// PUT the file to session.upload_path, then listen for media:ready over WebSocket
// Multipart (large files)
const mp = await orbit.media.initMultipart(mimeType, filename, actingUserId)
const media = await orbit.media.completeMultipart(mp.session_id, mimeType, size, actingUserId)
// Access
const file = await orbit.media.get(mediaId, actingUserId)
const { url } = await orbit.media.getDownloadUrl(mediaId, actingUserId)
const { url } = await orbit.media.getStreamUrl(mediaId, actingUserId)
const meta = await orbit.media.getMetadata(mediaId, actingUserId)
const status = await orbit.media.getStatus(mediaId, actingUserId)
// Management
await orbit.media.process(mediaId, actingUserId) // trigger reprocessing
await orbit.media.delete(mediaId, actingUserId)
await orbit.media.grantAccess(mediaId, { app_user_id, access_type }, actingUserId)
await orbit.media.listAccess(mediaId, actingUserId)orbit.realtime
// Session lifecycle
const session = await orbit.realtime.createSession({ type: 'chat_session', context_id? }, userId)
await orbit.realtime.endSession(session.id, userId)
// Orchestration helpers
await orbit.realtime.chatToCall(session.id, callId, userId)
await orbit.realtime.callToMeeting(session.id, meetingId, userId)
await orbit.realtime.meetingToChat(session.id, convId, userId)
// Generic transition
await orbit.realtime.transition(session.id, transitionType, contextId, userId)
// Events & context
await orbit.realtime.emitEvent(session.id, { event_type, payload? }, userId)
await orbit.realtime.updateContext(session.id, { conversation_id?, call_id?, meeting_id? }, userId)
const ctx = await orbit.realtime.getContext(session.id, userId)
const transitions = await orbit.realtime.listTransitions(session.id, userId)orbit.webhooks
// Setup
const wh = await orbit.webhooks.create('https://yourapp.com/webhooks/orbit')
await orbit.webhooks.subscribe(wh.id, 'message.sent')
await orbit.webhooks.subscribe(wh.id, 'call.ended')
// Verify signature in your handler
import crypto from 'crypto'
const expected = crypto.createHmac('sha256', wh.secret).update(rawBody).digest('hex')
if (sig !== `sha256=${expected}`) return res.status(401).end()
// Publish, replay, debug
await orbit.webhooks.publish('message.sent', { message_id, conversation_id })
await orbit.webhooks.replay(eventId)
const deliveries = await orbit.webhooks.listDeliveries(wh.id)
const attempts = await orbit.webhooks.getAttempts(deliveries[0].id)
// Maintenance
await orbit.webhooks.rotateSecret(wh.id)
await orbit.webhooks.update(wh.id, { is_active: false })orbit.billing
const { balance, currency } = await orbit.billing.getWallet()
await orbit.billing.topUp(amount)
await orbit.billing.getTransactions()
await orbit.billing.recordUsageEvent({ event_type, quantity, metadata? })
await orbit.billing.getUsage()
await orbit.billing.getAggregates()
await orbit.billing.getInvoices()
await orbit.billing.getInvoice(invoiceId)
await orbit.billing.createAlert({ type: 'low_balance', threshold: 5.00 })
await orbit.billing.getAlerts()Error handling
import { OrbitServerError } from '@orbitconnect/server'
try {
const user = await orbit.users.get(userId)
} catch (err) {
if (err instanceof OrbitServerError) {
console.error(err.status, err.code, err.message)
}
}Examples
Runnable examples live in examples/. Run them with:
# from sdk/orbit-server/
npm run example:basic
npm run example:group
npm run example:user
npm run example:call
npm run example:meeting
npm run example:media
npm run example:realtime
npm run example:webhook
npm run example:fullBuild
npm run build # compile to dist/
npm run dev # watch mode
npm run typecheck # type-check without emittingLicense
MIT
