Skip to main content

Intents reference

You never mutate agent state directly. You send a typed intent, the server reduces it, and the result comes back as a patch on the state stream. This page is the full catalogue of intents — one per Command in the babelconnect.v1 contract — with the TypeScript and Go method that sends each one, and the part of AgentView it moves.

:::tip The loop Every row is the same shape: you call the method → the server reduces it → a patch updates AgentView. Read State & events first if that loop isn't familiar yet. :::

Method names differ only by language convention — TypeScript is camelCase on BabelconnectClient, Go is PascalCase on the bcclient.Client. They map one-to-one.

Session & identity

IntentWhat it doesResult in AgentViewTypeScript · Go
RegisterAnnounce reachability + arm the media path, and load the agent's deployment data (presence options, caller-ID numbers, phonebook, feature config).config, agentregister(caps?) · Register(caps...)
Set WebRTCToggle the in-browser phone on/off.agentsetWebrtc(on) · SetWebrtc(on)
Set agent numberSet the external phone to bridge to.agentsetAgentNumber(num) · SetAgentNumber(num)
Set presenceSwitch presence ("available" or a pause reason).agentsetPresence(name) · SetPresence(name)
Set display-asChoose the outbound number presented to the consumer.agentsetDisplayAs(num) · SetDisplayAs(num)

Presence values are deployment-defined: read the list from AgentView.agent.presenceOptions — each has a name to pass to setPresence, a display label, and an available flag — rather than hard-coding pause reasons. Don't confuse this with agent.presence (an AgentState): that's the coarse, server-computed status bucket for a status icon — available, in_call, ringing, wrap_up, paused, busy, offline — which you render but never set. setPresence moves the agent's chosen presence (reflected in presenceName / presenceLabel); the server derives the presence bucket from that plus what the agent is actually doing.

Outbound caller ID: the numbers the agent can present are AgentView.agent.availableNumbers; the current selection is agent.displayAs, and setDisplayAs changes it.

Number format: phone numbers (to, from, displayAs, transfer targets) are E.164 — a leading +, country code, then the national number, no spaces or punctuation (e.g. +49301234567), as in the examples.

Where calls ring: setWebrtc and setAgentNumber choose the agent's audio path. With WebRTC enabled (setWebrtc(true)) the agent's call leg routes to the in-browser phone; with it disabled, the backend bridges the call to the agent's external number (setAgentNumber) instead. AgentView.agent.webrtcEnabled tells you which is active. An agent needs one of the two — WebRTC on, or an agent number set — to be reachable; with neither, calls can't reach them.

Register capabilities: TypeScript defaults the list, so register() sends ["webrtc"]; Go is variadic with no default, so pass it explicitly — Register("webrtc"). Note that registering marks the agent WebRTC-reachable on the backend regardless of the list, so its call leg routes to this client. A control-only client has no media leg to carry that audio — follow register() with setWebrtc(false) + setAgentNumber(...) to bridge calls to an external phone instead (see Control only → reachability).

Calls

IntentWhat it doesResult in AgentViewTypeScript · Go
Place callDial out; the agent's own leg auto-answers. Go's positional args are to (destination), displayAsTo (what the consumer sees), displayAsFrom (what the agent sees), record.callUpsertplaceCall(to, opts?) · PlaceCall(to, displayAsTo, displayAsFrom, record)
AnswerAccept a RINGING call by id.callUpsert (→ in-progress)answerCall(id) · Answer(id)
HangupEnd / reject a call by id.callRemovehangup(id) · Hangup(id)
MuteMute or unmute your leg.callUpsertmute(id, on) · Mute(id, on)
HoldHold or retrieve a call.callUpserthold(id, on) · Hold(id, on)
Send digitsSend DTMF into the call.— (tones only)sendDigits(id, digits) · SendDigits(id, digits)
TransferHand the call to a number, agent, or queue (warm = attended).callUpsert / callRemovetransfer(id, to, opts?) · Transfer(id, to, agentID, applicationID, warm)
Reset line statusClear a blocked line (busy / unreachable).agentresetLineStatus() · ResetLineStatus()

Answering: answerCall(id) does more than flip state — the SDK takes the ringing call's webrtcOffer, runs it through your media leg to produce the WebRTC SDP answer, and sends that. So answering needs a media leg; a control-only client (no mediaFactory) raises no_media instead.

Place-call options: to is the destination number being dialled; displayAsTo is the service number the consumer sees (the outbound caller ID); displayAsFrom is the number shown to the agent. record: true starts the recording from answer (instead of calling startRecording later). In TypeScript these are opts keys (placeCall(to, { displayAsTo, displayAsFrom, record })); in Go they're positional, in this order — PlaceCall(to, displayAsTo, displayAsFrom, record) — so it's easy to swap the two caller-ID arguments by mistake. The TypeScript opts also accepts a session map — opaque CTI correlation, the same keys the embed bridge sets via session.set (see SMS session); the Go signature omits it.

Transfer targets: set the field that matches the destination — an external number as to, another agent as agentId, or a queue / voice application as applicationId. In TypeScript, to is the second argument and agentId / applicationId are opts keys (transfer(id, to, { agentId })); in Go all three are positional params — Transfer(id, to, agentID, applicationID, warm). warm: true is an attended (warm) transfer; false is blind (cold).

Warm transfer is a two-step consult. An attended (warm: true) transfer isn't a single call: first addConferenceMember to pull the target into a conference (the customer is parked on hold while you consult privately), then transfer(id, …, { warm: true }) to complete — that unholds the customer and drops your own leg, leaving them connected to the target. Calling the warm transfer before the target is in a conference returns the no_conference error. A blind (warm: false) transfer is the single hand-off, no consult.

Because of that, the two transfers gate on different config flags: a blind transfer is gated by config.calls.allowTransfer (the cold-transfer action), but a warm transfer runs through a conference, so gate that control on config.calls.allowConference — with conferencing disabled there's no consult step, and the warm transfer can't happen.

DTMF: sendDigits sends RFC-2833 tones to the far end — valid characters are 09, *, #, and AD. The digits string can carry several at once (e.g. "1234#"), sent in sequence; it needs an active call.

Reset line status: if the agent's external phone goes busy, unreachable, or declines a call, the line can get stuck so new calls no longer reach the agent. resetLineStatus clears that blocked state — confirmed back via an agent presence patch — so they can take calls again.

Wrap-up (after-call work)

IntentWhat it doesResult in AgentViewTypeScript · Go
Extend wrap-upAdd seconds to the ACW countdown.wrapUpwrapUpExtend(seconds) · WrapUpExtend(seconds)
Cancel wrap-upEnd after-call work early.wrapUpwrapUpCancel() · WrapUpCancel()

Wrap-up: wrapUpExtend adds time to the after-call-work countdown — the TypeScript form defaults to 30 seconds (wrapUpExtend()), while Go takes an explicit int32. Show the extend / cancel controls only when wrapUp.canExtend / wrapUp.canCancel are set, and render the countdown locally from remainingSeconds (the server doesn't tick it per second; reconcile to each wrapUp patch). See the wrap-up walkthrough for the full sequence.

Wrap-up (ACW) is the timed after-call-work window: when a call ends the server may start it, emitting a wrapUp patch with active: true and a remainingSeconds countdown. Show the extend / cancel controls only when WrapUpStatus.canExtend / canCancel are set.

Recording

IntentWhat it doesResult in AgentViewTypeScript · Go
Start recordingBegin recording the current call.callUpsertstartRecording(id) · StartRecording(id)
Stop recordingStop recording.callUpsertstopRecording(id) · StopRecording(id)
Flag recordingToggle the "flagged" tag on the recording.callUpsertflagRecording(id) · FlagRecording(id)
Set recording tagsReplace the recording's tags.callUpsertsetRecordingTags(id, tags) · SetRecordingTags(id, tags)

Recording: gate these on AgentView.agent.canRecord. The call's recording flag, recordingId, recordingTags, and recordingFlagged reflect the current state; pick tags from agent.availableTags. When agent.alwaysRecordOutbound is set, outbound calls record automatically — and placeCall's record option starts recording from answer without a separate startRecording.

Conferencing

IntentWhat it doesResult in AgentViewTypeScript · Go
Start conferenceOpen a conference around the current call.conferenceUpsertstartConference(hold?) · StartConference(hold)
Add memberInvite an agent or a number.conferenceUpsertaddConferenceMember(opts) · AddConferenceMember(agentID, number)
Kick memberModerator removes a member.conferenceUpsertkickConferenceMember(id) · KickConferenceMember(id)
Hold memberModerator holds / unholds a member.conferenceUpsertholdConferenceMember(id, on) · HoldConferenceMember(id, on)
Mute memberModerator mutes / unmutes a member.conferenceUpsertmuteConferenceMember(id, on) · MuteConferenceMember(id, on)
End conferenceModerator ends the whole conference.conferenceRemoveendConference() · EndConference()
Leave conferenceHang up only your own leg.conferenceUpsert / conferenceRemoveleaveConference() · LeaveConference()

Conferencing: the agent who calls startConference becomes the moderator — gate the moderator-only controls (kick / hold / mute member, end) on Conference.iAmModerator. Each member is an agent or an external number with its own state and onHold; endConference finishes it for everyone, while leaveConference drops only your own leg. startConference opens the conference around your current call (you need an active call, or it's rejected with no_call); its hold flag parks that call while you add members and consult before bridging them together. You don't have to call it explicitly: addConferenceMember starts a conference for you if none is active, and that auto-start parks the current call on hold — which is why a warm transfer is just addConferenceMember(target) then transfer, with no separate startConference.

Messaging (SMS)

IntentWhat it doesResult in AgentViewTypeScript · Go
Send SMSSend a message; optionally a from and CTI session.smsUpsertsendSms(to, text, opts?) · SendSms(to, text, from, session)
Set conversation openOpen or close (resolve) a thread.smsUpsertsetConversationOpen(id, open) · SetConversationOpen(id, open)
Mark conversation readClear the unread count on a thread.smsUpsertmarkConversationRead(id) · MarkConversationRead(id)

SMS from & session: from is the sending number (which of your numbers the message goes out from); session attaches opaque CTI correlation — the same data the embed bridge sets via session.set.

Sending is the same operation on either surface: the sendSms stream intent and POST /v1/agent/sms send the identical message, and the unary REST form returns the upserted SmsConversation summary directly.

Reference data (unary — not intents)

These are request/response calls, not stream intents: they fetch reference data and return it directly, without producing a patch. They're also the operations exposed over REST/OpenAPIGET /v1/agent/history, GET /v1/agent/sms/thread, and GET /v1/agent/phonebook (plus GET /v1/agent/state for the snapshot and POST /v1/agent/sms to send a message), each with the same bearer token (see Authentication).

OperationWhat it returnsTypeScript · Go
Get historyRecent call records (paged).getHistory(max=50, page=1) · GetHistory(ctx, max, page)
Get SMS threadMessages in one conversation (paged).getSmsThread(id, max=50, page=1) · GetSmsThread(ctx, id, max, page)
Get phonebookContact entries (paged, searchable).getPhonebook(max=200, page=1, query='') · GetPhonebook(ctx, max, page, query)

Paging is 1-based offset paging: pass max (page size — default 50, or 200 for the phonebook) and page (default 1). The responses carry only the list — there's no total or hasMore — so you've reached the end when a page returns fewer than max items (or none). getPhonebook also takes a query to filter contacts server-side.

// Fetch every page — there's no total, so stop on the first short page.
async function allHistory(pageSize = 50) {
const out = [];
for (let page = 1; ; page++) {
const batch = await bc.getHistory(pageSize, page);
out.push(...batch);
if (batch.length < pageSize) return out; // a short (or empty) page is the last one
}
}

The Go pattern is identical — loop GetHistory(ctx, max, page) (or GetSmsThread / GetPhonebook), incrementing page until a call returns fewer than max records.

SMS thread: getSmsThread's id is normally the SmsConversation.id, but it also accepts the peer's phone number — handy for opening a thread for a contact before any conversation exists yet. Messages come back oldest-first, ready to render top-to-bottom. (markConversationRead and setConversationOpen need the actual conversation id, though — only getSmsThread takes the peer.)

History entries (CallRecord) carry an id, direction, from / to (with a contact phonebook label when known), time (unix seconds), durationMs (call length in milliseconds), and hasRecording + a recordingUrl for playback (set only when hasRecording).

Phonebook is also available without a fetch: AgentView.agent.phonebook is a register-time snapshot (dial-from contacts + recent numbers). Each PhonebookEntry is just a label + number, where label is the contact name — or the literal "recent" for a recently-dialed number, so you can separate the two in the UI. Use getPhonebook for a paged, searchable refresh.

See also