API reference
Spin up video, voice, live-stream and broadcast sessions, then pull back structured AI notes and tasks — the same data your team sees in-app. JSON in, JSON out, over HTTPS.
Base URL
Every endpoint below is relative to a versioned base URL. Use the staging host while you build, then flip a single constant to go live.
Live traffic, real minutes billed.
Sandbox for integration testing — never billed, sessions expire in ~5 minutes.
Use your same key from the dashboard on either host. Sessions created against staging are marked test, expire after ~5 minutes, and are excluded from your usage and cost.
Authentication
Pass your secret key as a bearer token on every request. Generate and rotate keys in the dashboard. Never ship a secret key in client-side code.
Authorization: Bearer vk_live_9f2a…Quick start
Create your first session in one call. Copy, paste in your key, run.
curl https://api.vanillameet.com/v1/sessions \
-H "Authorization: Bearer $VANILLA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"medium": "video",
"mode": "group",
"capacity": 6,
"recording": true
}'const res = await fetch("https://api.vanillameet.com/v1/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VANILLA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ medium: "video", mode: "group", capacity: 6 }),
});
const session = await res.json();
console.log(session.join_url);Create a session
Returns a single shareable join link, capped by capacity. Attach any metadata and it comes back on every related webhook.
POST /v1/sessions
Authorization: Bearer <api_key>
{
"medium": "video" | "voice" | "live_stream" | "broadcast" | "chat",
"mode": "1:1" | "group",
"capacity": 6,
"recording": false,
"metadata": { "reference_id": "abc123" },
"features": { "screen_share": false, "recording": false }
}
200 OK
{
"session_id": "sess_9f2a…",
"join_url": "https://vanillameet.com/j/9f2a",
"expires_at": "2026-07-24T18:30:00Z"
}Meeting features
Turn in-call actions on or off per meeting with an optional features object on POST /v1/sessions. Any key you omit stays enabled — pass false to hide that control for everyone in the call.
screen_sharePresent / share screenchatIn-call text chatrecordingRecord the callcaptionsLive captionsreactionsEmoji reactionsraise_handRaise handcelebrateCelebrate 🎉effectsVideo effects (blur, backgrounds)inviteInvite others mid-callnotesPost-call AI notes + transcriptiondata_saverAuto-pause video on poor networks (on by default){
"medium": "video",
"features": { "screen_share": false, "recording": false }
}data_saver is on by default: on a sustained poor connection it automatically pauses that participant's outgoing video to keep audio clear, then restores it on recovery — audio is never dropped. Set { "data_saver": false } to disable the automatic behaviour for a meeting.
Manage a live call
Control a call while it's happening with PATCH /v1/sessions/{id}. Changes are pushed live to everyone in the room.
- •Switch the medium mid-call — e.g. escalate a
chattovoiceorvideo, or drop video down to voice. - •Toggle features live (mute chat, disable recording, …).
- •End the call for everyone with
{ "action": "end" }.
# switch a chat room up to a video call, live
PATCH /v1/sessions/{session_id}
{ "medium": "video" }
# end it for all participants
PATCH /v1/sessions/{session_id}
{ "action": "end" }Medium behaviour on join: video = camera + mic on · voice = mic on, camera off · chat = text-first, camera + mic off (anyone can turn them on to escalate).
Branded call links
Drop people straight into a call with a link — pre-filled identity, your brand, and where to send them afterwards. Append these query params to https://vanillameet.com/call. No SDK needed.
codeThe meeting/room (required). Same code = same call.fullnameParticipant name — skips the lobby name step (name= also works).prefixHonorific before the name (e.g. Dr), excluded from the avatar initials.emailAttendee email, used for post-call notes / recap.brandColorAccent colour — hex (4f46e5 or %234f46e5), a CSS name (blue), or rgb().logoUrlhttps logo that replaces ours in the lobby, call, and notes page.titleMeeting title shown in the lobby and notes.noteson / off — AI notes + transcription for this call (default on).returnUrlWhere to send people on leave — https or /path (redirect= alias).private1 = private meeting; the host admits guests from a waiting room.https://vanillameet.com/call
?code=CODE
&fullname=Ada%20Lovelace
&prefix=Dr
&email=ada@acme.com
&brandColor=4f46e5
&logoUrl=https://cdn.acme.com/logo.svg
&title=Acme%20Consult
¬es=on
&returnUrl=https://app.acme.com/appointmentsEncode spaces as %20, and drop the # on hex colours (or encode it as %23). logoUrl and returnUrl must be https and are validated — a bad value falls back to the default. Get a code from POST /v1/sessions (it's the last path segment of the returned join_url).
List sessions
Filter your session history by date range and medium. Results are paginated newest-first.
GET /v1/sessions?since=2026-07-01&until=2026-07-24&medium=video
200 OK
{
"data": [
{ "session_id": "sess_9f2a…", "medium": "video", "minutes": 42, "ended_at": "2026-07-20T15:04:00Z" }
],
"has_more": false
}Get notes for a session
Structured AI notes and the per-participant task breakdown — the same content the host sees in-app. Available once session.notes_ready fires.
GET /v1/sessions/{session_id}/notes
200 OK
{
"session_id": "sess_9f2a…",
"ready": true,
"summary": "Kickoff for the Q3 launch…",
"decisions": ["Ship the beta on the 14th"],
"tasks": [
{ "text": "Draft the launch email", "owner": "Jane" }
]
}ready is false with empty fields until the call ends and notes are generated (also signalled by the session.notes_ready webhook).
Pricing & billing
You prepay a balance and only draw down what you use — billed per participant-minute when each call ends, tiered by medium. No per-minute vendor markup; roughly half of Agora HD.
Example: a 4-person, 30-minute video call = 120 participant-minutes = $0.24 (+ $0.40 if notes are on). Set { "features": { "notes": false } } to skip the notes fee.
A funded balance is required to create sessions — an empty balance returns 402 insufficient_credit. Top up in the dashboard (local payment via Bachs). Test for free on the staging host.
Errors
Errors return the right HTTP status with a stable machine-readable code and a human message.
{
"error": {
"code": "unauthorized",
"message": "Missing or invalid API key."
}
}invalid_requestA field is missing or malformed.unauthorizedMissing or invalid API key.forbiddenKey lacks access to this resource.insufficient_creditBalance empty — top up to create sessions.not_foundNo session with that id.rate_limitedToo many requests — back off and retry.server_errorSomething broke on our end. Safe to retry.Rate limits
The default limit is 120 requests/minute per key. Every response carries your current budget in headers — read them and back off on 429.
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1721840400Webhooks
Subscribe to lifecycle events to track usage or route tasks into your own system. Add endpoints in the dashboard; every delivery is signed so you can verify it came from us.
session.startedsession.participant_joinedsession.participant_leftsession.endedsession.recording_readysession.notes_readytask.assigned
{
"event": "session.notes_ready",
"session_id": "sess_9f2a…",
"metadata": { "reference_id": "abc123" },
"created_at": "2026-07-20T15:05:00Z"
}