REST API
Base URL https://api.applooma.dev/v1. Everything an SDK does is available over HTTP, so you can integrate from any language.
Authentication#
Server endpoints authenticate with your project's key pair, sent as two headers on every request:
x-applooma-key: ak_your_keyx-applooma-secret: as_your_secret
Server side only
These headers must never leave your backend. A client that holds your secret can issue unlimited tokens against your account.
Conventions#
- Request and response bodies are JSON. Send
Content-Type: application/json. - Every error looks the same:
{ "error": "human readable message" }, sometimes with a machine-readablecode. - Timestamps are ISO 8601 strings in UTC.
roomNameis your channel name and must match[a-zA-Z0-9_-]{1,64}.- Channels are isolated per project. A channel called
lobbyin one project has nothing to do withlobbyin another.
Issue a token#
The one endpoint every integration needs. Call it from your server after you have decided the user is allowed in.
POST /v1/token
| Field | Type | Required | Description |
|---|---|---|---|
roomName | string | Yes | The channel to join. [a-zA-Z0-9_-]{1,64}. |
identity | string | Yes | Unique id for this participant, 1–128 characters. Reusing an identity disconnects the earlier connection. |
role | string | No | One of host, cohost, audience. Defaults to host. |
metadata | object | No | Arbitrary JSON attached to the participant and visible to everyone in the channel. Maximum 2 KB. |
country | string | No | Pins a new channel to the AppLooma media server for this ISO 3166 country. Normally leave it out: the SDKs route from the device. |
region | string | No | Pins a new channel to a specific media server. Normally leave it out. |
Smart routing
The SDKs route automatically from the device: when joinChannel is given an applooma.dev URL, the SDK connects to the nearest AppLooma media server, and the first participant fixes the server for the channel so everyone in it meets on the same one. No backend change is needed, and your server should normally not send country or region — they pin the room. Always pass the wsUrl from this response to the SDK; never hard-code it.
curl -X POST https://api.applooma.dev/v1/token \-H "x-applooma-key: ak_your_key" \-H "x-applooma-secret: as_your_secret" \-H "Content-Type: application/json" \-d '{"roomName": "lobby","identity": "user-42","role": "host","metadata": { "name": "Ayesha" }}'
{"token": "eyJhbGciOi...","wsUrl": "wss://rtc.applooma.dev","region": "eu","roomName": "lobby","identity": "user-42","role": "host","expiresIn": 21600,"expiresAt": "2026-09-08T20:00:00.000Z"}
Tokens last six hours
A token authorises joining, not the whole session — a call already in progress is not interrupted when the token expires. To rejoin after that, request a new token.
Errors: 400 invalid roomName, identity or metadata; 401 bad key pair; 403 with code ACCOUNT_SUSPENDED; 429 with code QUOTA_EXCEEDED when the monthly minutes are used up.
Channels#
GET /v1/rooms
Lists channels in your project that currently have participants.
{"rooms": [{"roomName": "lobby","participantCount": 3,"createdAt": "2026-09-08T13:55:02.000Z","maxParticipants": 500}]}
GET /v1/rooms/:roomName/participants
Lists who is currently in one channel.
{"participants": [{ "identity": "user-42", "name": "Ayesha", "joinedAt": "2026-09-08T13:55:10.000Z" }]}
POST /v1/rooms/:roomName/participants/:identity/role
Changes one participant’s role while they stay connected. Their SDK turns the microphone on or off to match and fires onRoleChanged; no rejoin, no gap in audio, no new billing session. This is how a listener takes a seat in a voice room.
{ "role": "cohost" } // "host" | "cohost" | "audience"
{ "roomName": "room-42", "identity": "user-7", "role": "cohost" }
Returns 404 when the participant is not in that room.
POST /v1/rooms/:roomName/participants/:identity/remove
Disconnects one participant. Their SDK receives onDisconnected.
{ "roomName": "room-42", "identity": "user-7", "removed": true }
Live streaming#
POST /v1/streams/ingest
Creates an RTMP or WHIP endpoint so an external encoder — OBS, a hardware encoder, a camera — can publish into a channel.
| Field | Type | Required | Description |
|---|---|---|---|
roomName | string | Yes | Channel the encoder publishes into. |
name | string | No | Participant name for the encoder. Defaults to obs-ingest. |
type | string | No | "rtmp" or "whip". Defaults to rtmp. |
{"ingestId": "IN_abc123","url": "rtmp://ingest.applooma.dev/x","streamKey": "sk_live_...","roomName": "lobby","status": "ready"}
DELETE /v1/streams/ingest/:ingestId
Removes an ingest endpoint. Returns { "deleted": true }.
POST /v1/streams/push
Composites a channel and pushes it to up to three external RTMP destinations, such as YouTube, Facebook or Twitch.
| Field | Type | Required | Description |
|---|---|---|---|
roomName | string | Yes | Channel to composite. |
urls | string[] | Yes | One to three rtmp:// or rtmps:// destinations. |
{"pushId": "EG_abc123","roomName": "lobby","destinations": 2,"status": "active"}
POST /v1/streams/push/:pushId/stop
Stops a push. Returns { "pushId": "...", "status": "stopped" }, or 404 if the id is unknown, already stopped, or belongs to another project.
Virtual gifting#
GET /v1/gifts
The active gift catalogue for your project, for rendering a gift picker.
{"gifts": [{ "id": "gft_1", "name": "Rose", "imageUrl": "https://...", "coinPrice": 10 }]}
POST /v1/gifts/send
Records a gift and broadcasts it to everyone in the channel in real time. Call this from your server, after you have debited the sender.
| Field | Type | Required | Description |
|---|---|---|---|
roomName | string | Yes | Channel to broadcast in. |
giftId | string | Yes | Id from the catalogue. |
sender | string | Yes | Identity of the sender. |
receiver | string | Yes | Identity of the receiver. |
quantity | number | No | Integer 1–9999. Defaults to 1. |
{ "txId": "gtx_1", "coinTotal": 100, "sentAt": "2026-09-08T14:02:00.000Z" }
GET /v1/gifts/leaderboard
Top senders and receivers. Add ?roomName=lobby to scope it to one channel.
Dashboard endpoints#
These are what the console itself uses. They authenticate with a user session (Authorization: Bearer <jwt>) rather than an API key pair, and are documented for completeness — most integrations never need them.
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/plans | Public plan catalogue. No authentication. |
| GET | /v1/apps | Projects you own or that are shared with you. |
| POST | /v1/apps | Create a project. |
| POST | /v1/apps/:appId/keys/:keyId/rotate | Rotate a key pair. The new secret is returned once. |
| PATCH | /v1/apps/:appId/keys/:keyId | Enable or disable a key pair. |
| GET | /v1/stats | Usage across all your projects. |
| GET | /v1/apps/:appId/sessions | Session history, paginated. |
| GET | /v1/apps/:appId/gifts | Manage the gift catalogue. |
| GET | /v1/apps/:appId/webhooks | Manage webhook endpoints. |
| GET | /v1/account | The signed-in account, including its current plan. |
Not available#
Cloud recording endpoints exist in the routing table but return 501 with code RECORDING_NOT_AVAILABLE. AppLooma does not store customer media. If you need recording, capture it in your own infrastructure, or talk to us about a customer-owned storage bucket.