REST · v1

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.

Production

Live traffic, real minutes billed.

Staging

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.

header
Authorization: Bearer vk_live_9f2a…

Quick start

Create your first session in one call. Copy, paste in your key, run.

cURL
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
  }'
Node
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
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 screen
chatIn-call text chat
recordingRecord the call
captionsLive captions
reactionsEmoji reactions
raise_handRaise hand
celebrateCelebrate 🎉
effectsVideo effects (blur, backgrounds)
inviteInvite others mid-call
notesPost-call AI notes + transcription
data_saverAuto-pause video on poor networks (on by default)
disable presenting + recording
{
  "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 chat to voice or video, or drop video down to voice.
  • Toggle features live (mute chat, disable recording, …).
  • End the call for everyone with { "action": "end" }.
PATCH /v1/sessions/{id}
# 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).

List sessions

Filter your session history by date range and medium. Results are paginated newest-first.

GET /v1/sessions
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/{id}/notes
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.

Video / live-stream / broadcast$0.002 / participant-minute
Voice$0.001 / participant-minute
Chat$0.0005 / participant-minute
AI notes (per call)+ $0.40 flat — only when notes are on
Staging sandboxFree (api.staging host) — not billed

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.

401 Unauthorized
{
  "error": {
    "code": "unauthorized",
    "message": "Missing or invalid API key."
  }
}
400invalid_requestA field is missing or malformed.
401unauthorizedMissing or invalid API key.
403forbiddenKey lacks access to this resource.
402insufficient_creditBalance empty — top up to create sessions.
404not_foundNo session with that id.
429rate_limitedToo many requests — back off and retry.
500server_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.

response headers
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1721840400

Webhooks

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.started
  • session.participant_joined
  • session.participant_left
  • session.ended
  • session.recording_ready
  • session.notes_ready
  • task.assigned
POST to your endpoint
{
  "event": "session.notes_ready",
  "session_id": "sess_9f2a…",
  "metadata": { "reference_id": "abc123" },
  "created_at": "2026-07-20T15:05:00Z"
}