API documentation

REST API v1 — https://api.openlagoon.com/api/v1

Authentication

Every request (except GET /health and POST /tenants/register) requires your API key in the X-API-Key header.

curl -H "X-API-Key: YOUR_API_KEY" https://api.openlagoon.com/api/v1/rooms

All responses are JSON: {"success": true|false, "message": "...", "data": ...}

Rooms

Create video meeting rooms and mint join tokens for your users. Temporary rooms expire 8 hours after creation; scheduled rooms at end_time; persistent rooms never.

POST/rooms
{
  "name": "Strategy call",            // required
  "room_type": "temporary",            // temporary | scheduled | persistent
  "max_participants": 10,
  "access_type": "public",             // public | password | whitelist
  "recording_enabled": false,
  "chat_enabled": true,
  "waiting_room_enabled": false,
  "start_time": "2026-06-10 15:00:00", // scheduled rooms
  "end_time":   "2026-06-10 16:00:00"
}
// → { "data": { "id": "42", "room_id": "AbC123XyZ", "url": "https://dash.openlagoon.com/room/AbC123XyZ" } }
GET/rooms — list your rooms
GET/rooms/{id} — fetch one (numeric id or room_id)
PUT/rooms/{id} — update settings
DELETE/rooms/{id} — delete (soft)

Join tokens

POST/rooms/{id}/token
{ "participant_name": "Ada", "can_publish": true, "can_subscribe": true }
// → { "data": { "token": "<livekit-jwt>", "room_id": "AbC123XyZ", "url": "..." } }

Pass password for password rooms. Use the token with the LiveKit client SDK, or just send users to the room url.

POST/rooms/{id}/validate-access — pre-check a participant's access

Recordings

POST/rooms/{id}/recording/start
POST/rooms/{id}/recording/stop
GET/rooms/{id}/recording/status
GET/recordings — list
GET/recordings/{id} — detail
DELETE/recordings/{id}

Recording must be enabled on the room (recording_enabled: true). Subscribe to the recording.ready webhook to know when playback is available.

Scheduling

Read your booking system (session types, live slot availability, bookings) and cancel bookings. Bookings are created by guests through your public booking pages.

GET/scheduling/types — session types with booking URLs, schedules, access levels
GET/scheduling/types/{id}/slots — bookable slots (?from=YYYY-MM-DD optional)
// → { "data": { "timezone": "America/New_York", "duration_minutes": 30,
//      "slots": [ { "start": "2026-06-10T14:00:00+00:00", "end": "...", "start_host": "2026-06-10 10:00" } ] } }
GET/scheduling/bookings?status=upcoming|past|cancelled (default upcoming)
POST/scheduling/bookings/{code}/cancel — cancel; auto-refunds paid bookings via Stripe

Webhooks

Get notified when things happen. Deliveries are POSTed as JSON with an HMAC signature and retried with backoff (up to 5 attempts).

POST/webhooks
{ "url": "https://example.com/hooks/openlagoon", "events": "booking.created,recording.ready" }
// → { "data": { "id": 1, "secret": "shown-once-store-it" } }
GET/webhooks — list (with delivered/failed counts)
PUT/webhooks/{id} — change url/events or is_active
DELETE/webhooks/{id}

Events

booking.created · booking.cancelled · booking.rescheduled · recording.ready — or * for all.

Verifying signatures

// Header: X-OpenLagoon-Signature: sha256=<hex>
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
$valid = hash_equals($expected, $_SERVER['HTTP_X_OPENLAGOON_SIGNATURE'] ?? '');

Custom domains

POST/domains{ "domain": "book.yourco.com" }

Point a CNAME at dash.openlagoon.com (subdomains) or an A record at the platform IP (root domains), then verify.

GET/domains · POST/domains/{id}/verify · DELETE/domains/{id}

Analytics

GET/analytics/overview — rooms, participants, bookings, recordings at a glance
GET/analytics/rooms/{id} — per-room participant activity
GET/analytics/usage — plan limits + 6-month rooms/bookings trend

Account

POST/tenants/register{ "name": "...", "email": "..." } → returns your API key (once). Dashboard sign-in is passwordless via magic link.
GET/tenants/profile · PUT/tenants/profile

Errors

CodeMeaning
400Bad request / validation failed (see errors)
401Missing or invalid X-API-Key
403Plan limit reached or access denied
404Resource not found (or not yours)
409Conflict (duplicate, already exists)
500Server error — contact support