Skip to main content

MediaSFU API Reference for AI Coding Tools

This page gives an AI coding assistant, or a developer in a hurry, everything needed to build a working MediaSFU integration from one context window. The same content is available as plain text at /docs/llms-full.txt.

MediaSFU is real-time video, voice, AI agents, phone (SIP/PSTN) calls, recording, live translation and HLS in one platform. You call its HTTP API from your server and render rooms with an SDK (React, Angular, Vue, React Native, Expo, Flutter, Kotlin, Swift, Unity) or the @mediasfu/widgets web components.

Ground rules​

  • Base URL: https://mediasfu.com. Every API path starts with /v1/.
  • Auth header: Authorization: Bearer <api-username>:<api-key>. Both values are at mediasfu.com/api-keys. The key is 64 characters.
  • Server only. Call the API from your backend. Never put the API key in browser, mobile or desktop code. For a browser that must act directly, issue a short-lived, scoped disposable key.
  • Environments: a sandbox key creates sandbox rooms (IDs start with s), a production key creates production rooms (IDs start with p, billed). Start with sandbox.
  • Actions: each resource takes an action field: in the JSON body for POST, in the query string for GET.
  • Responses: success is HTTP 2xx with success: true and the payload beside it. Failure is { "success": false, "error": "…", "code": "NOT_FOUND" }. Branch on code: BAD_REQUEST, UNAUTHORIZED, FORBIDDEN, NOT_FOUND, CONFLICT, RATE_LIMITED, INTERNAL_ERROR.
  • Retries: send an Idempotency-Key header (8–128 visible ASCII characters) on room create and join so a retry returns the same room.

Rooms​

Everything happens in a room: meetings, webinars, broadcasts, phone calls and agent sessions.

DoRequest
CreatePOST /v1/rooms {"action":"create","userName":"host","duration":30,"capacity":5,"eventType":"conference"}
JoinPOST /v1/rooms {"action":"join","userName":"guest1","meetingID":"s1a2b3c4"}
ListGET /v1/rooms?action=get&eventStatus=active&startIndex=0&pageSize=20
Get oneGET /v1/rooms?action=get&meetingID=s1a2b3c4
CountsGET /v1/rooms/stats
End and removePOST /v1/rooms {"action":"delete","meetingID":"s1a2b3c4"}

Create fields:

FieldRequiredValues
userNameyesHost name, unique among your active rooms
durationyesMinutes, 5–1440
capacityyesMaximum participants
eventTypeyesconference, webinar, broadcast, chat
scheduledDatenoISO 8601 start time
secureCodenoHost secret; generated if omitted
recordOnlynotrue for a recording-only room
recordingParamsnorecordingAudioSupport, recordingVideoSupport, recordingHLSSupport, recordingPreferredOrientation
meetingRoomParamsnoaudioSetting, videoSetting, screenshareSetting (allow/approval/disallow), chatSetting, targetResolution (QnHD, sd, hd, fhd, qhd), addCoHost
supportSIP, directionSIPnoLet phone callers into the room: true; inbound, outbound or both
webhookUrl, webhookSecretnoWebhook for this room only (see Webhooks)

A successful create returns roomName, meetingID, secureCode and publicURL. Send people to publicURL for a ready-made room, or pass the create/join result to an SDK.

Use QnHD resolution for low-bandwidth audiences (rural clinics, mobile data).

Show the room in your app​

React (mediasfu-reactjs). The browser calls your backend; your backend adds the API key and forwards the same payload to POST https://mediasfu.com/v1/rooms.

import { ModernMediasfuGeneric } from 'mediasfu-reactjs';
import type { CreateRoomOnMediaSFUType, JoinRoomOnMediaSFUType } from 'mediasfu-reactjs';

const viaBackend = (path: string) => async ({ payload }: { payload: unknown }) => {
const response = await fetch(path, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error(`MediaSFU request failed: ${response.status}`);
return response.json();
};

export function Meeting() {
return (
<ModernMediasfuGeneric
connectMediaSFU
createMediaSFURoom={viaBackend('/api/mediasfu/create-room') as CreateRoomOnMediaSFUType}
joinMediaSFURoom={viaBackend('/api/mediasfu/join-room') as JoinRoomOnMediaSFUType}
/>
);
}

The backend routes are in Copy-paste recipes and Secure backend. Other SDKs: SDK guides. No SDK at all: participants over HTTP (WHIP/WHEP).

AI agents and phone calls​

  1. Add a number. Bring your own SIP number with POST /v1/sipconfigs {"action":"create","contactNumber":"+15551234567","provider":"twilio","supportSipActive":true,"supportSipNameCalls":true,"allowOutgoing":true}. Attach an AI agent to the number in the dashboard (Agents). MediaSFU-provided numbers are for outgoing calls without an agent.

  2. Inbound: callers to that number reach the attached agent. No code needed.

  3. Outbound: a host must be connected in a live room created with "supportSIP": true. Then:

    curl -X POST https://mediasfu.com/v1/sipcall/outgoingCall \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $MEDIASFU_API_USERNAME:$MEDIASFU_API_KEY" \
    -d '{"calledDid":"+15557654321","callerIdNumber":"+15551234567","roomName":"s1a2b3c4","initiatorName":"host","callPurpose":"Confirm tomorrow 10am appointment","callObjective":"Confirmed or rescheduled"}'

    callerIdNumber is your SIP number (E.164). initiatorName is alphanumeric and must be the name of someone already in the room. Optional context for the agent: callPurpose (≤500), callObjective (≤300), referenceId (≤100), urgency (low/normal/high/urgent), callScript (≤2000), customData (up to 20 keys).

  4. Control a live call with its sipCallId:

    DoRequest
    Read stateGET /v1/sipcall/:sipCallId/state
    Start or stop the agentPOST /v1/sipcall/:sipCallId/start-agent or /stop-agent
    Hand to a person or backPOST /v1/sipcall/:sipCallId/switch-source {"targetType":"human","humanName":"Pat"}
    Hold, unholdPOST /v1/sipcall/:sipCallId/hold, /unhold
    EndPOST /v1/sipcall/:sipCallId/end
    List callsGET /v1/sipcall/list?page=1&limit=20&direction=incoming
    TotalsGET /v1/sipcall/summary

    A 429 carries retryAfterMs; wait that long before the next control.

Recordings​

Turn recording on with recordingParams at create (or from the room UI). After recording.completed arrives, list files:

GET /v1/recordings?action=get&name=<meetingID> returns recordings[] with url, size and duration. Without name it pages through all recordings (startIndex, pageSize).

Webhooks​

Room events. Set an account default with POST /v1/eventssettings {"action":"update","webhookUrl":"https://example.com/hooks/mediasfu","webhookSecret":"…"} (use "action":"create" the first time, with type, audioSetting and videoSetting), or per room with webhookUrl on create. The URL must be public HTTPS with no port, credentials or fragment. Send an empty string to clear.

MediaSFU POSTs JSON:

{
"id": "6f1c…",
"event": "room.ended",
"roomName": "s1a2b3c4",
"timestamp": "2026-10-13T18:00:00.000Z",
"data": {}
}

Events: room.created, room.ended, participant.joined, participant.left, recording.started, recording.paused, recording.resumed, recording.stopped, recording.processing, recording.completed, recording.failed, meeting.expired, translation.started.

Headers: x-webhook-event, x-webhook-id, x-webhook-timestamp and, when a secret is set, x-webhook-signature = lowercase hex HMAC-SHA256 of the raw body. Return 2xx quickly; a 5xx is retried three times (2 s, 8 s, 30 s). Use id to ignore duplicates.

import crypto from 'node:crypto';

export function verifyMediaSfuWebhook(rawBody, signature, secret) {
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return typeof signature === 'string' && signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}

Call events. Set webhookUrl on the SIP number's config in the dashboard. MediaSFU POSTs eventType sip:callStateUpdate (with sipCallId, roomName, calledDid, currentState.status, currentState.durationSeconds, currentState.callEnded), sip:conversationSummary (summary text and transcript), sip:humanInterventionRequired and sip:callbackRequested. A call has ended when currentState.callEnded is true.

Usage and balance​

DoRequest
Remaining minutesGET /v1/balance (optional scope=sandbox or production)
One room's itemised usageGET /v1/room/logs?roomName=<meetingID>&fieldKey=usageLogs&envelope=1
One room's recording usagesame, with fieldKey=recordUsageLogs

Each usage entry records the interval with producers, viewers, quality and rate. Prices: mediasfu.com/pricing.

Common mistakes​

SymptomCauseFix
401 on every callHeader is Bearer <key> only, or username and key swappedSend Bearer <api-username>:<api-key>
401 "demo key"Demo keys can't create rooms over the APIUse a sandbox or production key
401 with a production keyCalled over httpUse https://mediasfu.com
Production create rejected from a hosted appThe calling domain isn't allowedAdd it with /v1/domains
Outbound call 404 "Initiator not found"Nobody named initiatorName is in the roomJoin the room as that name first
Outbound call 400 "does not support SIP"Room created without supportSIPCreate with "supportSIP": true
Webhook URL rejectedNot public HTTPS, or has a portUse a public HTTPS hostname on 443
API key in browser network tabFrontend calls MediaSFU directlyRoute through your backend or use a disposable key

Use MediaSFU from an AI assistant​

Give your assistant MediaSFU knowledge and account tools with the MCP server:

npx -y @mediasfu/mcp-sdk-tools

Account actions (rooms, calls, recordings, webhooks, usage) use your own key from the client's environment. Setup: MediaSFU MCP.

More​