onepizza.io API

A RESTful + WebSocket API for creating and managing video meetings. Built for developers, agents, and teams.

Version: 3.0.0 Updated: March 19, 2026 Base URL: https://onepizza.io
Authentication overview:
REST API calls — API key in x-api-key header (obtain from Dashboard → API Keys)
Admin meeting actionsx-admin-token header (returned when meeting is created)
Dashboard / user account endpoints — session cookie (set on login, used automatically by browsers)
Guest meetings — no authentication required (rate-limited to 5/hour per IP)

Table of Contents

1. Account & Authentication 2. API Keys 3. Meetings (API) 4. Guest Meetings 5. Scheduled Meetings 6. Meeting Settings & Controls 7. Participant Controls 8. Meeting History 9. Webhooks 10. Real-time Events (Socket.IO) 11. Waiting Room Events 12. WebRTC Flow 13. ICE Server Config 14. Company Accounts 15. Billing 16. Errors & Rate Limits 17. Environment Variables 18. Admin Analytics API 19. Quick Start 20. Features Overview

1. Account & Authentication

POST /api/auth/register

Create a new account. Returns the account API key. A welcome email is sent if RESEND_API_KEY is configured.

FieldTypeRequiredDescription
emailstringYesEmail address (must be unique)
passwordstringYesMinimum 8 characters
accountTypestringNo"personal" (default) or "company"
companyNamestringIf companyRequired when accountType is "company"
curl -X POST /api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"secret123"}'

# Response
{
  "message": "Account created",
  "email": "you@example.com",
  "apiKey": "mk_abc123...",
  "accountType": "personal"
}
POST /api/auth/login

Log in to your account. Sets a session cookie used by the dashboard.

FieldTypeDescription
emailstringAccount email
passwordstringAccount password
POST /api/auth/logout

Destroy the current session and clear the session cookie.

GET /api/auth/me session

Get the current authenticated user's profile. Used by the dashboard to verify login state.

{
  "id": 42,
  "email": "you@example.com",
  "accountType": "personal",
  "balance": "25.0000",
  "company_id": null,
  "is_admin": false,
  "created_at": "2026-03-01T12:00:00Z"
}
POST /api/auth/forgot-password

Request a password reset email. Rate-limited to 3 requests per 15 minutes per IP. Silently succeeds even if email not found (prevents user enumeration).

FieldTypeDescription
emailstringThe email address to send the reset link to
GET /api/auth/reset-password

Validate a password reset token. Called client-side to check if the token from the email link is still valid (tokens expire after 1 hour).

Query ParamDescription
tokenThe reset token from the email link
POST /api/auth/reset-password

Set a new password using a valid reset token.

FieldTypeDescription
tokenstringReset token from the email link
passwordstringNew password (min 8 characters)
POST /api/auth/change-password session

Change the current user's password. Requires authentication. Sends a "password changed" security email on success.

FieldTypeDescription
currentPasswordstringCurrent account password
newPasswordstringNew password (min 8 characters)

2. API Keys

API keys authenticate REST API calls via the x-api-key header. Multiple keys can be active simultaneously. Keys are prefixed mk_.

GET /api/user/keys session

List all API keys for the current user.

{
  "keys": [
    { "id": 1, "key": "mk_abc123...", "label": "Production", "is_active": true, "created_at": "2026-03-01T12:00:00Z" }
  ]
}
POST /api/user/keys session

Generate a new API key.

FieldTypeDescription
labelstringOptional label for this key (e.g. "Production")
DELETE /api/user/keys/:id session

Revoke and permanently delete an API key. Any requests using the deleted key will immediately return 401.

3. Meetings (API)

POST /api/meetings

Create a new meeting (instant or scheduled). Requires an active API key. Credits are deducted when the meeting ends based on duration and peak participant count.

FieldTypeRequiredDescription
titlestringNoMeeting title (default: "Untitled Meeting")
scheduledAtstringNoISO 8601 UTC date to schedule the meeting for a future time. If omitted, the meeting is immediately active.
muteOnJoinbooleanNoAuto-mute new participants (default: false)
videoOffOnJoinbooleanNoAuto-disable video for new participants (default: false)
maxParticipantsnumberNoMaximum concurrent participants (default: 50)
# Instant meeting
curl -X POST /api/meetings \
  -H "Content-Type: application/json" \
  -H "x-api-key: mk_yourkey" \
  -d '{"title": "Team Standup"}'

# Response
{
  "meetingId": "abc-defg-hij",
  "adminToken": "d4e5f6...",
  "joinUrl": "/join/abc-defg-hij",
  "title": "Team Standup",
  "status": "active",
  "settings": { "muteOnJoin": false, "videoOffOnJoin": false, "maxParticipants": 50, "locked": false, "waitingRoom": false }
}

# Scheduled meeting
curl -X POST /api/meetings \
  -H "Content-Type: application/json" \
  -H "x-api-key: mk_yourkey" \
  -d '{"title":"Weekly Sync","scheduledAt":"2026-04-01T14:00:00Z"}'

# Response
{
  "meetingId": "xyz-abcd-efg",
  "adminToken": "a1b2c3...",
  "joinUrl": "/join/xyz-abcd-efg",
  "title": "Weekly Sync",
  "scheduledAt": "2026-04-01T14:00:00.000Z",
  "status": "scheduled",
  "settings": { "muteOnJoin": false, "videoOffOnJoin": false, "maxParticipants": 50, "locked": false, "waitingRoom": false }
}
GET /api/meetings

List all currently active and scheduled meetings for the authenticated API key's user.

{
  "meetings": [
    { "meetingId": "abc-defg-hij", "title": "Team Standup", "status": "active", "participantCount": 3, "createdAt": 1710000000000 },
    { "meetingId": "xyz-abcd-efg", "title": "Weekly Sync", "status": "scheduled", "scheduledAt": "2026-04-01T14:00:00.000Z", "participantCount": 0 }
  ]
}
GET /api/meetings/:meetingId

Get details of a specific meeting including current participants and settings.

{
  "meetingId": "abc-defg-hij",
  "title": "Team Standup",
  "status": "active",
  "createdAt": 1710000000000,
  "participantCount": 2,
  "participants": [
    { "participantId": "a1b2c3", "name": "Alice", "isMuted": false, "isVideoOff": false, "isScreenSharing": false, "joinedAt": 1710000001000 }
  ],
  "settings": { "muteOnJoin": false, "videoOffOnJoin": false, "maxParticipants": 50, "locked": false, "waitingRoom": false }
}
DELETE /api/meetings/:meetingId admin token

End a meeting, disconnect all participants, and trigger billing. Requires x-admin-token.

curl -X DELETE /api/meetings/abc-defg-hij \
  -H "x-api-key: mk_yourkey" \
  -H "x-admin-token: d4e5f6..."

4. Guest Meetings

Guest meetings are created without authentication. They are in-memory only (no billing, no persistence) and rate-limited to prevent abuse.

POST /api/meetings/guest

Create a guest meeting with no API key. Limited to 5 meetings per hour per IP address. This endpoint powers the homepage "Start a meeting" button.

FieldTypeDescription
titlestringOptional meeting title (default: "Quick Meeting")
curl -X POST /api/meetings/guest \
  -H "Content-Type: application/json" \
  -d '{"title": "Quick Sync"}'

# Response
{
  "meetingId": "qwerty-abc",
  "adminToken": "uuid...",
  "joinUrl": "/join/qwerty-abc",
  "title": "Quick Sync"
}
Note: Guest meetings are not persisted, not billed, and are lost when the server restarts. For production use, create a real account and use POST /api/meetings.

5. Scheduled Meetings

How scheduling works:
• Pass scheduledAt (ISO 8601 UTC) to POST /api/meetings
• Meeting stays in scheduled status until the scheduled time arrives
• At the scheduled time the meeting auto-activates — participants can join
• If someone tries to join early, they get an error with the scheduled time
• Cancel a scheduled meeting with DELETE /api/meetings/:meetingId
GET /api/meetings/scheduled/list

List all scheduled (not yet started) meetings for the authenticated API key's user.

curl /api/meetings/scheduled/list -H "x-api-key: mk_yourkey"

# Response
{
  "meetings": [
    { "meetingId": "xyz-abcd-efg", "title": "Weekly Sync", "scheduledAt": "2026-04-01T14:00:00.000Z", "status": "scheduled", "createdAt": 1710000000000 }
  ]
}

6. Meeting Settings & Controls

All settings and lock/unlock endpoints require the x-admin-token header.

PATCH /api/meetings/:meetingId/settings admin token

Update meeting settings live. Changes are broadcast to all participants via the meeting:settings-updated Socket.IO event.

FieldTypeDescription
titlestringRename the meeting
muteOnJoinbooleanAuto-mute new joiners
videoOffOnJoinbooleanAuto-disable video for new joiners
maxParticipantsnumberChange the participant cap
lockedbooleanLock or unlock the meeting
waitingRoombooleanEnable or disable the waiting room
POST /api/meetings/:meetingId/lock admin token

Lock the meeting — no new participants can join until unlocked.

POST /api/meetings/:meetingId/unlock admin token

Unlock the meeting, allowing participants to join again.

POST /api/meetings/:meetingId/invite admin token

Generate a one-use invite link for a new participant.

FieldTypeDescription
namestringOptional — pre-fills the participant's name on the join page
{
  "joinUrl": "/join/abc-defg-hij?invite=x1y2z3&name=Bob",
  "inviteToken": "x1y2z3"
}

7. Participant Controls

All endpoints require both x-api-key and x-admin-token. Actions are applied immediately via Socket.IO to the target participant.

POST /api/meetings/:meetingId/participants/:participantId/mute admin token

Force-mute a specific participant. The participant receives an admin:mute event and their audio is disabled client-side.

POST /api/meetings/:meetingId/participants/:participantId/unmute admin token

Send an unmute request to a specific participant.

POST /api/meetings/:meetingId/participants/:participantId/kick admin token

Remove a participant immediately. They receive an admin:kick event with a reason.

FieldTypeDescription
reasonstringOptional message shown to the removed participant
POST /api/meetings/:meetingId/mute-all admin token

Mute every participant simultaneously. Broadcasts meeting:all-muted to the room.

8. Meeting History

GET /api/meetings/history session

Get the meeting usage history for the authenticated user (or their company). Includes duration, peak participants, and credit cost per meeting.

curl /api/meetings/history -b session_cookie

# Response
{
  "meetings": [
    {
      "id": 1,
      "meeting_id": "abc-defg-hij",
      "title": "Team Standup",
      "started_at": "2026-03-19T10:00:00Z",
      "ended_at": "2026-03-19T10:30:00Z",
      "duration_minutes": "30.00",
      "peak_participants": 4,
      "cost_usd": "0.0120"
    }
  ]
}

9. Webhooks

Register HTTPS endpoints to receive real-time notifications about meeting events. Webhooks are sent as POST requests with a JSON body.

GET /api/webhooks session

List all configured webhook endpoints for the current user.

{
  "webhooks": [
    { "id": 1, "url": "https://your-app.com/webhook", "events": ["meeting.ended","participant.left"], "created_at": "2026-03-01T12:00:00Z" }
  ]
}
POST /api/webhooks session

Register a new webhook endpoint.

FieldTypeDescription
urlstringHTTPS URL to receive webhook events
eventsstring[]Array of event names to subscribe to (see below)

Available Events

EventDescription
meeting.endedFired when a meeting ends (via DELETE or all participants leave)
participant.joinedFired when a participant joins the meeting
participant.leftFired when a participant disconnects

Webhook Payload

{
  "event": "meeting.ended",
  "meetingId": "abc-defg-hij",
  "timestamp": 1710000000000,
  "data": {
    "title": "Team Standup",
    "durationMinutes": 30,
    "peakParticipants": 4,
    "costUsd": "0.0120"
  }
}
DELETE /api/webhooks/:id session

Remove a webhook endpoint. Events will no longer be sent to that URL.

10. Real-time Events (Socket.IO)

Connect to the Socket.IO server at the base URL. The same server handles both signaling and meeting state.

import { io } from "socket.io-client";
const socket = io("https://onepizza.io");

Client → Server

EventPayloadDescription
join-meeting{ meetingId, name, isAdmin, adminToken }Join a meeting. Pass isAdmin: true and the adminToken to join as host.
signal:offer{ to: participantId, offer }Send a WebRTC offer to a specific participant
signal:answer{ to: participantId, answer }Send a WebRTC answer
signal:ice-candidate{ to: participantId, candidate }Send an ICE candidate
media:toggle-audio{ isMuted: boolean }Broadcast your mute state to all participants
media:toggle-video{ isVideoOff: boolean }Broadcast your video state
media:screen-share{ isScreenSharing: boolean }Broadcast screen-share state
raise-hand{ isHandRaised: boolean }Raise or lower your hand
react{ emoji: string }Send a floating emoji reaction (👍 ❤️ 😂 🎉 👏)
recording:broadcast-started{ hostName: string }Notify participants that the host has started recording
chat:message{ text: string }Send a chat message (max 2000 chars)

Server → Client

EventPayloadDescription
joined{ participantId, participants[], settings, title, isAdmin }Confirmed join — includes all current participants and meeting settings
participant:joined{ participantId, name, isMuted, isVideoOff, isAdmin }A new participant joined the room
participant:left{ participantId, name }A participant disconnected
participant:updated{ participantId, isMuted?, isVideoOff?, isScreenSharing?, isHandRaised? }A participant's media or hand state changed
signal:offer{ from: participantId, offer }Incoming WebRTC offer
signal:answer{ from: participantId, answer }Incoming WebRTC answer
signal:ice-candidate{ from: participantId, candidate }Incoming ICE candidate
admin:mute{}You were force-muted by the host
admin:unmute{}The host sent you an unmute request
admin:kick{ reason: string }You were removed from the meeting
meeting:ended{ reason: string }The meeting was ended by the host or admin
meeting:all-muted{}All participants were muted
meeting:settings-updated{ muteOnJoin, videoOffOnJoin, maxParticipants, locked, waitingRoom }Meeting settings changed
chat:message{ from: participantId, name, text, timestamp }A chat message was sent in the room
react{ participantId, emoji }An emoji reaction from a participant
recording:started{ hostName: string }The host has started recording — display consent notice
error{ message: string }Something went wrong (meeting not found, locked, full, or scheduled)

11. Waiting Room

When waiting room is enabled for a meeting (via settings), participants queue up before being admitted by the host.

Enable the waiting room via PATCH /api/meetings/:meetingId/settings with { "waitingRoom": true }.

Waiting Room Events (Client → Server)

EventPayloadDescription
waiting-room:join{ meetingId, name }Join the waiting queue (called automatically instead of join-meeting when waiting room is active)
waiting-room:admit{ meetingId, socketId }Host only — admit a waiting participant into the meeting
waiting-room:deny{ meetingId, socketId }Host only — deny and remove a participant from the queue

Waiting Room Events (Server → Client)

EventPayloadDescription
waiting-room:waiting{ message }Sent to the waiting participant — confirms they are in the queue
waiting-room:admitted{}You were admitted — proceed to join-meeting
waiting-room:denied{ message }The host declined your entry
waiting-room:participant-waiting{ socketId, name, count, removed? }Sent to host(s) — a participant is waiting or was admitted/denied

12. WebRTC Flow

The service uses a full mesh topology — each participant connects directly to every other participant via WebRTC peer connections.

1. You emit join-meeting → server responds with "joined" (includes all current participants)
2. For each existing participant, you create an RTCPeerConnection and send them an offer via signal:offer
3. They receive your offer → create an answer → send via signal:answer
4. Both sides exchange ICE candidates via signal:ice-candidate
5. Media (audio/video) flows directly peer-to-peer

Supported media tracks:
  - Audio:       Microphone (mutable)
  - Video:       Camera (toggleable, device-selectable)
  - Screen:      Screen share (replaces camera track temporarily)
  - Background:  Canvas-based blur filter (processed stream replaces camera track)
Mesh topology note: Full mesh scales to roughly 6–8 participants before performance degrades on average hardware. For larger meetings, consider an SFU (Selective Forwarding Unit) architecture.

13. ICE Server Configuration

GET /api/config/ice-servers

Returns the STUN/TURN server list used for WebRTC. The web client calls this automatically on join. If TURN credentials are configured via environment variables, they are included here.

# Default response (STUN only)
{
  "iceServers": [
    { "urls": "stun:stun.l.google.com:19302" },
    { "urls": "stun:stun1.l.google.com:19302" }
  ]
}

# With TURN configured
{
  "iceServers": [
    { "urls": "stun:stun.l.google.com:19302" },
    { "urls": "turn:your-turn-server.com:3478", "username": "...", "credential": "..." }
  ]
}

14. Company Accounts

Company accounts let multiple users share a single credit balance and API keys under one workspace. Any credit deducted by a member comes from the company balance.

POST /api/auth/register

Create a company account by setting accountType: "company". An invite code is returned, which members use to join.

curl -X POST /api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"owner@acme.com","password":"secret123","accountType":"company","companyName":"Acme Corp"}'

# Response
{
  "message": "Account created",
  "email": "owner@acme.com",
  "apiKey": "mk_abc123...",
  "inviteCode": "aBcDeFgHiJkL",
  "accountType": "company"
}
POST /api/company/join session

Join an existing company workspace using an invite code. The user's balance merges into the company balance.

FieldTypeDescription
inviteCodestringThe invite code from the company owner
curl -X POST /api/company/join \
  -H "Content-Type: application/json" \
  -b session_cookie \
  -d '{"inviteCode": "aBcDeFgHiJkL"}'

# Response
{ "message": "Joined Acme Corp", "companyId": 1, "companyName": "Acme Corp" }
GET /api/company/members session

List all members of the current user's company. Only available to company account owners.

{
  "members": [
    { "id": 1, "email": "owner@acme.com", "role": "owner", "joined_at": "2026-03-01T12:00:00Z" },
    { "id": 2, "email": "dev@acme.com", "role": "member", "joined_at": "2026-03-05T08:00:00Z" }
  ]
}
DELETE /api/company/members/:id session

Remove a member from the company. Only the company owner can do this.

15. Billing

Credits are consumed at meeting end based on duration × peak participants × the rate configured in the admin panel (default: $0.004/participant-minute). If a user belongs to a company, the company balance is used instead of the personal balance. A low-balance alert email is sent when balance drops below $2.00.

POST /api/billing/stripe/checkout session

Create a Stripe Checkout session to top up credits via credit/debit card. Redirects the user to Stripe's hosted checkout page.

FieldTypeDescription
amountUsdnumberAmount in USD to top up (min $5, max $1000)
curl -X POST /api/billing/stripe/checkout \
  -H "Content-Type: application/json" \
  -b session_cookie \
  -d '{"amountUsd": 20}'

# Response
{ "url": "https://checkout.stripe.com/c/pay/..." }
GET /api/billing/usdc/address session

Get the USDC (ERC-20) deposit address for your account. Each user/company gets a unique HD wallet address derived from the server's mnemonic. Send USDC on Ethereum mainnet to this address — the server polls for deposits and credits your balance automatically.

curl /api/billing/usdc/address -b session_cookie

# Response
{ "address": "0xABC123..." }
1 USDC = $1.00 USD credit. Minimum deposit: $5 USDC. Only Ethereum mainnet USDC (ERC-20) is supported.
GET /api/billing/history session

Get balance and transaction history for the current user or their company.

{
  "balance": "25.0000",
  "transactions": [
    { "id": 1, "amount_usd": "20.0000", "type": "stripe_topup", "description": "Stripe top-up $20", "created_at": "2026-03-10T12:00:00Z" },
    { "id": 2, "amount_usd": "-0.0120", "type": "meeting_charge", "description": "Meeting abc-defg-hij (30m, 4 participants)", "created_at": "2026-03-19T10:30:00Z" }
  ]
}
POST /api/billing/stripe/webhook

Stripe webhook receiver (internal use). Credits are added automatically when Stripe confirms a payment. Configured via STRIPE_WEBHOOK_SECRET.

16. Errors & Rate Limits

HTTP Error Responses

All errors return JSON: { "error": "<message>" }

StatusCause
400Validation error (missing field, bad format, e.g. scheduledAt must be in the future)
401Missing or invalid x-api-key, or not logged in
402Insufficient credits to start a meeting
403Missing or invalid x-admin-token
404Meeting, participant, or resource not found
409Conflict (email already in use, already in a company, etc.)
429Rate limit exceeded — slow down requests
500Internal server error

Rate Limits

Endpoint GroupLimit
Login / Register10 requests / 15 min per IP
Forgot / Reset password3 requests / 15 min per IP
Guest meeting creation5 requests / 1 hour per IP
API (authenticated)100 requests / 15 min per IP

17. Environment Variables

VariableRequiredDescription
DATABASE_PUBLIC_URLYesPostgreSQL connection string
SESSION_SECRETYesSession signing secret (min 32 chars, random)
ADMIN_EMAILYesAdmin account email (seeded on first start)
ADMIN_PASSWORDYesAdmin account password
APP_URLNoPublic base URL for email links (default: http://localhost:3000)
PORTNoHTTP server port (default: 3000)
NODE_ENVNodevelopment or production
ALLOWED_ORIGINSNoComma-separated CORS origins (default: http://localhost:3000)
RESEND_API_KEYNoResend API key for transactional emails (silently skipped if unset)
STRIPE_SECRET_KEYNoStripe secret key — disables card payments if unset
STRIPE_WEBHOOK_SECRETNoStripe webhook signing secret
STRIPE_PRICE_IDNoStripe Price ID for credit top-ups
TURN_URLSNoTURN server URL (e.g. turn:your-server.com:3478)
TURN_USERNAMENoTURN server username
TURN_CREDENTIALNoTURN server password
CRYPTO_MNEMONICNoBIP-39 mnemonic for HD wallet (USDC deposits) — 12-word phrase

18. Admin Analytics API

Server-side analytics endpoints for platform administrators. All endpoints require an active admin session (cookie-based).

GET /admin/api/analytics/features Admin

Feature usage breakdown for tracked events (screen share, recording, chat, reactions, etc.).

Query ParamTypeDefaultDescription
daysnumber30Lookback window in days
# Response
{
  "features": [
    { "feature": "screen_share", "total_uses": 142, "unique_users": 38, "active_days": 22 },
    { "feature": "recording", "total_uses": 67, "unique_users": 15, "active_days": 18 }
  ],
  "days": 30
}
GET /admin/api/analytics/errors Admin

Aggregated error log grouped by event type, route, and message.

Query ParamTypeDefaultDescription
daysnumber30Lookback window in days
# Response
{
  "errors": [
    { "event_type": "error.webrtc", "route": "/join", "message": "ICE connection failed", "count": 12, "last_seen": "2026-03-20T14:30:00Z" }
  ],
  "days": 30
}
GET /admin/api/analytics/realtime Admin

Live meeting state from server memory (no DB query). Shows all currently active meetings and aggregate counts.

# Response
{
  "activeMeetings": 3,
  "totalParticipants": 14,
  "activeRecordings": 1,
  "activeScreenShares": 2,
  "meetingList": [
    { "id": "abc-defg-hij", "title": "Team Standup", "participantCount": 5, "duration": 1200, "isRecording": true }
  ]
}
GET /admin/api/analytics/retention Admin

Week-over-week user retention for the last 12 weeks.

# Response
{
  "retention": [
    { "week": "2026-03-10", "users": 120, "retained": 84 },
    { "week": "2026-03-03", "users": 115, "retained": 79 }
  ]
}
GET /admin/api/analytics/health Admin

Server health metrics including memory usage, DB pool stats, and active meeting counts.

# Response
{
  "uptimeSeconds": 86400,
  "memoryMB": 128.5,
  "heapUsedMB": 95.2,
  "heapTotalMB": 140.0,
  "dbPoolTotal": 20,
  "dbPoolIdle": 15,
  "dbPoolWaiting": 0,
  "activeMeetings": 3,
  "scheduledMeetings": 1
}
GET /admin/api/analytics/peak-hours Admin

Meeting distribution by day-of-week and hour. Useful for capacity planning.

Query ParamTypeDefaultDescription
daysnumber90Lookback window in days
# Response
{
  "peakHours": [
    { "dow": 1, "hour": 10, "count": 45 },
    { "dow": 3, "hour": 14, "count": 38 }
  ],
  "days": 90
}

Tracked Event Types

The analytics system tracks the following event types. These are stored in the analytics_events table and queried by the endpoints above.

Event TypeTriggerMeta Fields
feature.screen_shareScreen share start/stop{ action: 'start'/'stop' }
feature.recordingRecording start/stop{ action: 'start'/'stop' }
feature.chatChat message sent{ length }
feature.chat_reactionChat message reaction{ emoji }
feature.reactionFloating emoji reaction{ emoji }
feature.hand_raiseHand raise toggled{ raised: true/false }
feature.captionsCaptions used{}
feature.waiting_roomWaiting room admit/deny{ action: 'admit'/'deny' }
meeting.participant_joinedParticipant joins meeting{ meetingId, participantCount }
meeting.createdMeeting created{ scheduled, muteOnJoin, waitingRoom }
meeting.endedMeeting ended{ durationMinutes, peakParticipants, costUsd }

19. Quick Start

1. Start a guest meeting (no account needed)

curl -X POST https://onepizza.io/api/meetings/guest \
  -H "Content-Type: application/json" \
  -d '{"title": "Quick Sync"}'
# Open the joinUrl in a browser to join

2. Create an account and get an API key

curl -X POST https://onepizza.io/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"secret123"}'
# Save the apiKey from the response

3. Create, manage, and end a meeting via API

API_KEY="mk_yourkey"
BASE="https://onepizza.io"

# Create
RESP=$(curl -s -X POST $BASE/api/meetings \
  -H "Content-Type: application/json" \
  -H "x-api-key: $API_KEY" \
  -d '{"title":"Team Standup"}')

MEETING_ID=$(echo $RESP | jq -r '.meetingId')
ADMIN_TOKEN=$(echo $RESP | jq -r '.adminToken')
echo "Join: $BASE/join/$MEETING_ID"

# Check participants
curl -s $BASE/api/meetings/$MEETING_ID -H "x-api-key: $API_KEY" | jq

# Mute a participant
curl -s -X POST $BASE/api/meetings/$MEETING_ID/participants/PARTICIPANT_ID/mute \
  -H "x-api-key: $API_KEY" \
  -H "x-admin-token: $ADMIN_TOKEN"

# End the meeting
curl -s -X DELETE $BASE/api/meetings/$MEETING_ID \
  -H "x-api-key: $API_KEY" \
  -H "x-admin-token: $ADMIN_TOKEN"

4. Schedule a future meeting

curl -X POST https://onepizza.io/api/meetings \
  -H "Content-Type: application/json" \
  -H "x-api-key: $API_KEY" \
  -d '{"title":"Daily Standup","scheduledAt":"2026-04-01T09:00:00Z"}'

5. Connect via Socket.IO (Node.js)

import { io } from "socket.io-client";

const socket = io("https://onepizza.io");

socket.emit("join-meeting", {
  meetingId: "abc-defg-hij",
  name: "Bot",
  isAdmin: false,
  adminToken: null,
});

socket.on("joined", ({ participantId, participants }) => {
  console.log("I am", participantId, "— others:", participants.map(p => p.name));
});

socket.on("participant:joined", p => console.log(p.name, "joined"));
socket.on("chat:message", ({ name, text }) => console.log(`${name}: ${text}`));

// Send a chat message
socket.emit("chat:message", { text: "Hello from the bot!" });

// Raise hand
socket.emit("raise-hand", { isHandRaised: true });

// Emoji reaction
socket.emit("react", { emoji: "👍" });

20. Features Overview

CategoryFeatureDescription
VideoWebRTC video callsFull mesh peer-to-peer video; camera device selection
VideoScreen sharingShare entire screen or application window
VideoBackground blurCanvas-based blur filter on your camera feed
VideoSpotlight / pinPin any participant to the main view; others go to a filmstrip
AudioMicrophone controlMute/unmute with device selection
AudioSpeaking indicatorBlue highlight on the currently speaking tile
CollaborationIn-meeting chatReal-time text chat visible to all participants
CollaborationEmoji reactionsFloating emoji reactions (👍 ❤️ 😂 🎉 👏) visible to everyone
CollaborationRaise handSignal to the host you want to speak; badge shown on your tile
CollaborationRecording consentHost can broadcast recording notice; all participants are notified
Host controlsParticipant managementMute, unmute, kick individual participants
Host controlsMute allSilence every participant with one action
Host controlsMeeting lockPrevent new participants from joining
Host controlsWaiting roomQueue participants; host admits or denies each one
SchedulingScheduled meetingsPlan meetings for a future time; auto-activate at start time
APIREST APIFull programmatic meeting management via API key auth
APISocket.IO eventsReal-time state sync for custom clients and bots
APIWebhooksPush notifications to your server for meeting and participant events
APIMeeting historyPer-meeting usage log with duration, participants, and cost
AccountsPassword resetEmail-based reset flow with 1-hour expiry
AccountsCompany workspacesMulti-user accounts with shared credits and invite codes
BillingUsage-based creditsCharged at meeting end: duration × peak participants × rate/min
BillingStripe paymentsTop up credits via credit/debit card (Stripe Checkout)
BillingUSDC cryptoTop up credits with USDC (ERC-20) via dedicated HD wallet address
InfrastructureSTUN/TURN supportConfigurable TURN credentials for NAT traversal
InfrastructureRate limitingPer-IP limits on auth, guest meetings, and API endpoints
InfrastructureCompressiongzip/brotli response compression
InfrastructureSecurity headershelmet.js CSP, HSTS, and other security headers
onepizza.io — onepizza.io