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_key
x-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-readable code.
  • Timestamps are ISO 8601 strings in UTC.
  • roomName is your channel name and must match [a-zA-Z0-9_-]{1,64}.
  • Channels are isolated per project. A channel called lobby in one project has nothing to do with lobby in 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

FieldTypeRequiredDescription
roomNamestringYesThe channel to join. [a-zA-Z0-9_-]{1,64}.
identitystringYesUnique id for this participant, 1–128 characters. Reusing an identity disconnects the earlier connection.
rolestringNoOne of host, cohost, audience. Defaults to host.
metadataobjectNoArbitrary JSON attached to the participant and visible to everyone in the channel. Maximum 2 KB.
countrystringNoPins a new channel to the AppLooma media server for this ISO 3166 country. Normally leave it out: the SDKs route from the device.
regionstringNoPins 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.

Request
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" }
}'
200 Response
{
"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.

200 Response
{
"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.

200 Response
{
"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.

Request
{ "role": "cohost" } // "host" | "cohost" | "audience"
200 Response
{ "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.

200 Response
{ "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.

FieldTypeRequiredDescription
roomNamestringYesChannel the encoder publishes into.
namestringNoParticipant name for the encoder. Defaults to obs-ingest.
typestringNo"rtmp" or "whip". Defaults to rtmp.
201 Response
{
"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.

FieldTypeRequiredDescription
roomNamestringYesChannel to composite.
urlsstring[]YesOne to three rtmp:// or rtmps:// destinations.
201 Response
{
"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.

200 Response
{
"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.

FieldTypeRequiredDescription
roomNamestringYesChannel to broadcast in.
giftIdstringYesId from the catalogue.
senderstringYesIdentity of the sender.
receiverstringYesIdentity of the receiver.
quantitynumberNoInteger 1–9999. Defaults to 1.
201 Response
{ "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.

MethodPathPurpose
GET/v1/plansPublic plan catalogue. No authentication.
GET/v1/appsProjects you own or that are shared with you.
POST/v1/appsCreate a project.
POST/v1/apps/:appId/keys/:keyId/rotateRotate a key pair. The new secret is returned once.
PATCH/v1/apps/:appId/keys/:keyIdEnable or disable a key pair.
GET/v1/statsUsage across all your projects.
GET/v1/apps/:appId/sessionsSession history, paginated.
GET/v1/apps/:appId/giftsManage the gift catalogue.
GET/v1/apps/:appId/webhooksManage webhook endpoints.
GET/v1/accountThe 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.