A Sautikit conference room is a named virtual space. Any call, inbound or outbound, can be placed into a room using the Conference voice action. Participants join independently, and the room starts as soon as the first participant with startOnEnter: true connects. You control entry/exit beeps, mute state per participant, participant capacity, hold music, and conference recording entirely through the Conference action parameters and status webhook events.
Room names are arbitrary strings scoped to your workspace. Choose names that are unique to a session: for example, use a database-generated meeting ID (meeting-${id}) rather than a static string, so separate meetings don't accidentally share a room.
startOnEnter: false keeps a participant in hold-music limbo until someone else joins with startOnEnter: true. This lets you model a "waiting room" where participants hear hold music until a host arrives. Set startOnEnter: true on the host's join action.
maxParticipants caps the room size. Callers who arrive after the cap is reached receive no further actions (the call stalls). Handle this in your webhook by checking participant count before returning a Conference action; you can instead return a Say + Hangup if the room is full.
To let a supervisor listen without speaking, return muted: true on the supervisor's Conference action. The supervisor hears all participants but cannot be heard. To barge in, your server would need to end the supervisor's muted call and re-dial them without muted.
Endpoints you call:
POST /v1/calls: dial a participant into the conference (outbound leg).GET /v1/calls/{call_sid}: confirm a participant's call status.GET /v1/calls: list all calls for a meeting session (filter by your room name if you embed it in a custom parameter).Voice actions used:
Conference: the core verb; places the call into a named room.Say: greet the participant before joining, or announce room-full errors.Play: play a custom hold audio track via waitUrl.Redirect: move a participant to a different room or back to an IVR.Hangup: eject a participant from the room.import express from "express";
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// Attach this URL to a Sautikit number as the routing_url
app.post("/conference/join", (req, res) => {
const roomName = req.query.room ?? "default-room";
const isHost = req.query.host === "1";
res.json({
actions: [
{
say: {
text: isHost
? `You are joining ${roomName} as the host.`
: `You are joining ${roomName}. Please wait for the host.`,
},
},
{
conference: {
name: roomName,
startOnEnter: isHost,
endOnExit: isHost, // end room when host leaves
beep: true, // audible join/leave tone
maxParticipants: 10,
waitUrl: "https://yourapp.example.com/hold-music.mp3",
statusEventsCallbackUrl: "https://yourapp.example.com/conference/events",
statusEvents: "start end join leave mute unmute",
},
},
],
});
});
// Conference status events from Sautikit
app.post("/conference/events", (req, res) => {
const { ConferenceName, StatusCallbackEvent, CallId } = req.body;
console.log(`[${ConferenceName}] event=${StatusCallbackEvent} call=${CallId}`);
// Update your participant list in the DB here
res.sendStatus(204);
});
app.listen(3000);curl -X POST "https://api.sautikit.com/v1/calls" \
-H "Authorization: Bearer $SAUTIKIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+254733000001",
"from": "+254700000001",
"action_url": "https://yourapp.example.com/conference/join?room=meeting-99"
}'When this outbound call is answered, Sautikit POSTs to action_url, your server returns the Conference action for meeting-99, and the dialled participant joins the room.
{
"actions": [
{
"conference": {
"name": "meeting-99",
"startOnEnter": true,
"record": true,
"beep": true,
"statusEventsCallbackUrl": "https://yourapp.example.com/conference/events",
"statusEvents": "start end join leave"
}
}
]
}When record: true is set on a Conference action, the entire room session is recorded. The recording URL is delivered via the status event callback when the conference ends.
Conference billing works at the individual call-leg level. Each participant in the room is a separate call leg, billed independently:
A three-person meeting with a total duration of 30 minutes, where all three participants were dialled out, costs 3 × 30 minutes × (outbound per-minute rate).
If conference recording is enabled (record: true), recording storage fees apply separately. See call recording use case for storage and retrieval details.
Hold time (before startOnEnter: true is set) is billed as call time for the participant sitting in the waiting state. Keep waiting periods short or notify participants via a different channel when the host has joined.
flags, collectDigits, and rotation support.status_url events.