Developer documentation

Use Bunch API keys to create short-lived meeting join links for your users. This guide covers setup, authentication, request and response formats, implementation examples, error handling, and production rollout guidance for web apps, mobile apps, internal tools, and backend services.

Base application URL

https://www.bunch.community

Meeting domain

https://meet.bunch.community

Short links (dashboard)

https://www.bunch.community/m/{code}

Primary integration endpoint

POST /api/jwt

Authentication

Bearer API key or x-api-key header

Integration overview

Your backend sends Bunch a room name and user details. Bunch validates the API key, issues a signed JWT, and returns a secure join URL that opens the branded Bunch Meet experience for that user.

  1. Create an API key from the Bunch dashboard.
  2. Store the raw key securely in your application backend or secrets manager.
  3. Call POST /api/jwt whenever you want to create a meeting join link.
  4. Set returnHost and/or returnGuest so each role returns to the right page in your app after leave or end meeting (required for reliable iframe embeds).
  5. Redirect the user to the returned joinUrl or open it in your app shell / iframe.

Create an API key

In Bunch, go to Dashboard → API keys and create a key for the system or product surface that will request meeting links.

  • Name the key after the integration that will use it, such as `mobile-app` or `crm-backend`.
  • Copy the raw key immediately after creation. It is only shown once.
  • Store the raw key in your secrets manager, environment variables, or backend config.
  • Do not embed API keys directly in browser code or ship them in mobile apps.

Authentication

Bunch supports two authentication styles for the JWT endpoint. Use one of the following on every request:

Authorization: Bearer YOUR_BUNCH_API_KEY
x-api-key: YOUR_BUNCH_API_KEY

Create a meeting JWT

Use the endpoint below whenever you want to generate a secure meeting join URL for an external user or an authenticated user in your own application.

POST https://www.bunch.community/api/jwt

Request body

FieldTypeRequiredDescription
roomNamestringYesMeeting room slug. Must be between 1 and 200 characters.
userNamestringYesDisplay name shown inside the meeting UI.
userEmailstringNoOptional participant email for context and downstream workflows.
userAvatarstringNoOptional absolute URL for the participant avatar image.
moderatorbooleanNoDefaults to false. Set to true when the participant should join as moderator.
returnUrlstringNoLegacy fallback return URL. Applied as returnHost when moderator is true, returnGuest when false.
returnHoststringNoHTTPS URL for hosts/moderators after leave or end meeting. Added to joinUrl automatically when set in the request.
returnGueststringNoHTTPS URL for guests (non-moderators) after they leave. Use on guest link flows or set both returnHost and returnGuest when issuing two links.

Example request

{
  "roomName": "team-standup",
  "userName": "Constance Oshafi",
  "userEmail": "constance@example.com",
  "userAvatar": "https://example.com/avatar.png",
  "moderator": true,
  "returnHost": "https://client.example.com/meetings/team-standup/done"
}

Success response

{
  "joinUrl": "https://meet.bunch.community/team-standup?jwt=eyJ...&returnHost=https%3A%2F%2Fclient.example.com%2Fdone&return=https%3A%2F%2Fclient.example.com%2Fdone",
  "roomName": "team-standup",
  "jwt": "eyJ...",
  "expiresIn": 7200
}

Use the joinUrl value exactly as returned. It already includes JWT and return URL query parameters when you pass returnHost, returnGuest, or returnUrl in the request body.

API vs dashboard links: POST /api/jwt returns a direct Meet URL with an embedded JWT. Dashboard host/guest link APIs return a short URL on www.bunch.community (see below). API-issued meetings skip the dashboard guest admission gate.

Short links and guest admission

When a logged-in host copies links from the Bunch dashboard, Bunch returns compact short URLs instead of long Meet URLs with JWT query strings. This makes links easier to share in chat, SMS, and email.

Short link format

https://www.bunch.community/m/Ab12Cd34
  • Host short links mint a fresh moderator JWT on each visit and redirect to meet.bunch.community.
  • Guest short links open the guest join page at /join/guest?c=Ab12Cd34 where the guest enters their display name.
  • Short links expire after 7 days. Host links can be regenerated from the dashboard at any time.

Dashboard link API responses

Session endpoints that create shareable links now return:

{
  "joinUrl": "https://www.bunch.community/m/Ab12Cd34",
  "shortUrl": "https://www.bunch.community/m/Ab12Cd34",
  "expiresIn": 7200
}

joinUrl and shortUrl are the same for dashboard flows. Copy either value for end users. Integrators using POST /api/jwt still receive the full Meet URL in joinUrl (no short link indirection).

Guest admission (dashboard meetings only)

Meetings created via the Bunch dashboard require host approval the first time each guest joins. After the host admits a guest once, that guest can rejoin with the same display name without waiting again (remembered in the browser). Meetings created via POST /api/jwt disable this gate — guests join directly with the JWT you issue.

  1. Guest opens the short link and submits their display name.
  2. POST /api/meetings/join-request creates a pending request (public, no auth).
  3. Host sees pending guests on the dashboard and admits or declines.
  4. On approval, the guest receives a Meet JWT and is redirected into the call.
// Guest requests to join (from /join/guest page)
POST https://www.bunch.community/api/meetings/join-request
{ "code": "Ab12Cd34", "displayName": "Alex" }

// Response when waiting for host
{ "status": "pending", "requestId": "clx..." }

// Poll until approved
GET https://www.bunch.community/api/meetings/join-request?requestId=clx...
{ "status": "approved", "joinUrl": "https://meet.bunch.community/..." }

// Host lists pending guests (session cookie)
GET /api/meetings/{meetingId}/join-requests

// Host admits or declines (session cookie)
POST /api/meetings/{meetingId}/join-requests/{requestId}
{ "action": "approve" }   // or "reject"

Live participants (session)

Logged-in hosts can query who is currently in an active meeting. This uses telemetry from the Meet client and updates when participants join or leave.

GET /api/meetings/{meetingId}/participants
Cookie: <bunch session cookie>

{
  "meeting": { "id": "...", "roomName": "team-standup", "isActive": true },
  "participants": [
    { "id": "...", "displayName": "Alex", "joinedAt": "2026-06-02T12:00:00.000Z" }
  ]
}

Return URLs — integrator requirements

After a participant leaves or a host ends the meeting, Bunch Meet redirects to the URL you provide. Pass return fields in the API request body; Bunch adds them to joinUrl automatically. Do not strip query parameters before sending users to Meet.

Body parameters (all meeting link APIs)

ParameterTypeUsed onDescription
returnHoststring (HTTPS)Host / moderator linksWhere hosts go after Leave or End meeting for all. Required for iframe embeds.
returnGueststring (HTTPS)Guest linksWhere guests go after they leave. Issue a separate guest link per room.
returnUrlstring (HTTPS)Any link APILegacy alias: maps to returnHost when moderator is true, returnGuest when false.

Endpoints that accept return fields

EndpointAuthReturn fields in body
POST /api/jwtAPI keyreturnHost, returnGuest, returnUrl (+ moderator selects host vs guest)
POST /api/meetings/joinBunch session cookiereturnHost, returnUrl
POST /api/meetings/guest-linkBunch session cookiereturnGuest, returnUrl (+ roomName, guestDisplayName, title)
POST /api/meetings/{id}/host-linkBunch session cookiereturnHost, returnUrl
POST /api/meetings/{id}/guest-linkBunch session cookiereturnGuest, returnUrl (+ optional guestDisplayName)

Join URL query parameters (set by Bunch)

When return fields are in the request body, the response joinUrl includes:

  • returnHost — used when the JWT has moderator: true
  • returnGuest — used when the JWT has moderator: false
  • return — legacy mirror of the role-specific URL (same destination)

Integrator checklist

  1. Create two links per room when you have both hosts and guests: one JWT with moderator: true + returnHost, one with moderator: false + returnGuest.
  2. Redirect or embed the full joinUrl from the API response — do not rebuild the URL without return params.
  3. Use absolute HTTPS return URLs on your own domains (HTTP localhost allowed for dev only).
  4. For iframe embeds, set returnHost and listen for bunch:meeting-ended on the parent page (see below).
  5. Do not append return= manually unless you control encoding; prefer API body fields.

Redirect users back after leaving a meeting

When a user clicks Leave meeting or a host clicks End meeting for all, Bunch Meet redirects them using a return URL stored when the meeting loaded. Use separate URLs for hosts and guests so each role lands on the right page in your product.

  • Hosts / moderators: pass returnHost in the JWT request body (or append returnHost= to the join URL).
  • Guests: pass returnGuest on guest-link endpoints or append returnGuest= to the guest join URL.
  • Legacy: returnUrl in the API body maps to returnHost or returnGuest based on moderator. A plain return= query param still works for either role when role-specific params are omitted.
// Host link (moderator)
const hostRes = await fetch("https://www.bunch.community/api/jwt", {
  method: "POST",
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    roomName: "team-standup",
    userName: "Host User",
    moderator: true,
    returnHost: "https://client.example.com/app/meetings/123/host-done",
  }),
});
const { joinUrl: hostJoinUrl } = await hostRes.json();

// Guest link (non-moderator) — separate JWT + returnGuest
const guestRes = await fetch("https://www.bunch.community/api/jwt", {
  method: "POST",
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    roomName: "team-standup",
    userName: "Guest",
    moderator: false,
    returnGuest: "https://client.example.com/app/meetings/123/thanks",
  }),
});
const { joinUrl: guestJoinUrl } = await guestRes.json();

Embedded meetings (iframe)

If you embed the joinUrl in an iframe, set returnHost on the host link so moderators return to your app when they end the call. The Meet client notifies the parent window via postMessage and also attempts window.top navigation when allowed.

<iframe
  id="bunch-meet"
  src={hostJoinUrl}
  allow="camera; microphone; fullscreen; display-capture"
  style="width: 100%; height: 100%; border: 0;"
/>

// Parent page — distinguish Leave vs End meeting for all
window.addEventListener("message", (event) => {
  if (event.data?.type !== "bunch:meeting-ended") return;
  const { returnUrl, source, action } = event.data;
  const endAction = action || source; // "leave" | "end"

  if (endAction === "end") {
    // Host clicked End meeting for all — mark session ended in your backend
    void markSessionEnded();
  }
  // Always navigate back (silent leave or post-end redirect)
  if (typeof returnUrl === "string" && returnUrl.startsWith("https://")) {
    window.location.assign(returnUrl);
  }
});

postMessage payload

{
  "type": "bunch:meeting-ended",
  "returnUrl": "https://client.example.com/doctor/done?action=end",
  "source": "end",
  "action": "end"
}
FieldValuesMeaning
sourceleave | endleave = user clicked Leave; end = host clicked End meeting for all (or conference ended for everyone)
actionleave | endSame as source — provided for convenience
returnUrlHTTPS URLYour returnHost or returnGuest URL with ?action=leave or ?action=end appended

Mobile WebView (return URL redirect)

Mobile apps embedding Bunch in a WebView do not receive postMessage. Instead, intercept the return URL when Meet redirects after leave or end. Bunch appends an action query parameter:

  • returnHost=https://example.com/doctor/done?action=leave — host or participant clicked Leave
  • returnHost=https://example.com/doctor/done?action=end — host clicked End meeting for all (all participants receive action=end on their role-specific return URL)
  • Same pattern applies to returnGuest for non-moderator links.
// React Native WebView — intercept return URL
<WebView
  source={{ uri: joinUrl }}
  onShouldStartLoadWithRequest={(req) => {
    const url = new URL(req.url);
    const action = url.searchParams.get("action");
    if (url.origin === "https://client.example.com" && action) {
      if (action === "end") markSessionEnded();
      navigateAway(url.pathname);
      return false;
    }
    return true;
  }}
/>

Session guest links (logged-in host)

When the host is logged into Bunch, use POST /api/meetings/guest-link with an optional returnGuest field. The response returns a short URL on www.bunch.community; return parameters are stored server-side and applied when the guest is admitted and redirected to Meet.

POST https://www.bunch.community/api/meetings/guest-link
Content-Type: application/json
Cookie: <bunch session cookie>

{
  "roomName": "team-standup",
  "guestDisplayName": "Alex",
  "returnGuest": "https://client.example.com/meetings/team-standup/thanks"
}

// Response
{
  "joinUrl": "https://www.bunch.community/m/Ab12Cd34",
  "shortUrl": "https://www.bunch.community/m/Ab12Cd34",
  "roomName": "team-standup",
  "expiresIn": 7200
}
  • Use absolute HTTPS URLs (localhost HTTP is allowed for development only).
  • Two links per room: issue one JWT with moderator: true + returnHost, and one with moderator: false + returnGuest.
  • Security: only set return URLs to domains you control; validate postMessage origins in production if needed.

cURL example

curl -X POST "https://www.bunch.community/api/jwt" \
  -H "Authorization: Bearer YOUR_BUNCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "roomName": "team-standup",
    "userName": "Constance Oshafi",
    "userEmail": "constance@example.com",
    "moderator": true,
    "returnHost": "https://client.example.com/meetings/team-standup/done"
  }'

JavaScript example

const response = await fetch("https://www.bunch.community/api/jwt", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.BUNCH_API_KEY}`,
  },
  body: JSON.stringify({
    roomName: "team-standup",
    userName: "Constance Oshafi",
    userEmail: "constance@example.com",
    moderator: true,
    returnHost: "https://client.example.com/meetings/team-standup/done",
  }),
});

const data = await response.json();

if (!response.ok) {
  throw new Error(data.error || "Failed to create Bunch meeting link");
}

// joinUrl already includes returnHost; redirect or embed it
return data.joinUrl;

Error handling

StatusMeaningTypical causeWhat to do
400Bad RequestMissing fields, invalid email, invalid avatar URL, or malformed JSON.Validate payloads before sending and log the response body during integration.
401UnauthorizedMissing API key or invalid API key.Confirm your key is present, active, and sent using Bearer auth or x-api-key.
403ForbiddenGuest join request was declined by the host.Ask the host to send a new guest link or admit the guest from the dashboard.
410GoneMeeting room was permanently closed.Create a new room name; closed rooms cannot be reused.
500Server misconfigurationJWT secrets missing or mismatched between Bunch API and Meet infrastructure.Ensure JWT_APP_ID and JWT_APP_SECRET match on bunch-web and bunch-meet-prosody. Retry later if the error persists.

Session-based web app flow

If your users are logged into Bunch in the browser, use session cookies instead of an API key:

POST /api/meetings/join
POST /api/meetings/guest-link
// Host join (session) — returns short URL
POST /api/meetings/join
{ "roomName": "team-standup", "title": "Standup", "returnHost": "https://www.bunch.community/dashboard" }
// { "joinUrl": "https://www.bunch.community/m/...", "shortUrl": "...", "expiresIn": 7200 }

// Guest share link (session) — short URL + host admission on first join
POST /api/meetings/guest-link
{
  "roomName": "team-standup",
  "guestDisplayName": "Alex",
  "returnGuest": "https://client.example.com/thanks"
}

Recommended production practices

  • Generate join links from your backend, not from public frontend code.
  • Rotate API keys when team ownership changes or if a key is exposed.
  • Log request failures with response status and body for troubleshooting.
  • Treat the returned JWT and join URL as sensitive, short-lived credentials.
  • Use room naming conventions that map cleanly to your own meetings or records.
  • Always pass returnHost / returnGuest (or returnUrl) when users must return to your app after a call.

Troubleshooting join failures

SymptomLikely causeFix
Jitsi username/password loginUser opened meet.bunch.community/room without ?jwt= in the URL.Always use the full joinUrl from the API or a valid short link — never send users to a bare room path.
Token authentication failedJWT_APP_SECRET mismatch between bunch-web and bunch-meet-prosody, or expired JWT (2h).Verify Fly secrets match on all Meet apps; regenerate links if older than expiresIn.
Not allowed to join this callroomName in URL does not match the room claim in the JWT (case or slug).Use roomName exactly as returned by the API; Bunch normalizes to lowercase slugs.
Guest stuck on waiting screenHost has not admitted the guest yet (dashboard meetings only).Host opens dashboard → active meeting → Admit. API meetings skip this step.

Frequently asked questions

How do I tell Leave apart from End meeting for all?

Check source or action on the bunch:meeting-ended postMessage (web iframe), or read the action query param on the return URL redirect (mobile WebView). Use end to mark a clinical session as formally ended; use leave when the user only stepped out of the call.

Do API integrations use short links?

No. POST /api/jwt returns a direct meet.bunch.community URL with JWT and return query parameters. Short links and guest admission apply to dashboard-created meetings only.

How long are Bunch JWTs valid?

The current integration response returns expiresIn: 7200, which is a 2-hour lifetime.

Can I embed the returned meeting link?

Yes. Load the joinUrl in an iframe or WebView. For hosts ending a call from an iframe, pass returnHost when creating the link and listen for postMessage events with type bunch:meeting-ended on the parent page. See Redirect users back after leaving a meeting above.

Can I create separate keys per integration?

Yes. That is the recommended approach so you can rotate or revoke one integration without affecting others.

Which return field should I use?

Prefer returnHost for moderators and returnGuest for guests. Use returnUrl only if you have a single integration path and set moderator per call. All three are optional but strongly recommended for production embeds and multi-page apps.

Start integrating Bunch

Create an API key, connect your backend, and start returning branded meeting links from your own product flows.

Log in to manage API keysBack to home