Skip to main content
A Pearl can be driven in one of two ways:
  • A Simple Pearl: a single opening sentence plus a flow script. Created with Create Pearl.
  • A Pearl (the focus of this guide): a graph of nodes connected by transitions, giving you full control over the conversation, branching, integrations, and post-call automation.
This page explains the node logic: how nodes connect, how the entry point and IDs work, how transitions branch, and what differs between the Voice and Text channels.
Endpoints for node-graph Pearls:

The mental model

A Pearl flow is a directed graph:
  • Each node does one thing (say something, call an API, send an email, transfer, end the call, …).
  • Each transition is a labeled edge from one node to another. The label is a plain-language condition that decides when that edge is taken.
  • The conversation starts at a single entry point and every path eventually reaches the one EndCall node.
You send the whole graph in the pearl.nodes array. You do not draw the graph visually, you describe it: every node lists its outgoing transitions, and each transition names the toNodeId it leads to. The nodes reference each other by ID, so the order inside the array does not matter.
The words “call” and “EndCall” are kept for both channels. On a Text Pearl they simply mean the conversation and the end-of-conversation node.

Node types

Every node has a nodeType (an integer). This is the single most important field: it decides what the node does and which settings object it must carry.
Sending a node type that is not allowed for the channel is rejected. For example an SMS node (50) or a TransferCall node (60) in a Text Pearl, or a HandoffChatToHuman node (31) in a Voice Pearl.

Anatomy of a node

Only fields marked 🧩 May support variables accept the {variableId} syntax. See Flow Variables for the full variable system.

Node IDs and transitions

This is the part people get wrong most often, so read it carefully. There are two kinds of IDs, and they behave differently.

Node IDs: you provide them

  • You choose nodeId. It is how the graph wires itself together: a transition points at a node through toNodeId, which must equal that node’s nodeId.
  • The nodeId must be unique within the flow and is capped at 20 characters. The name does not have to be unique.
  • Pick short, stable, meaningful IDs (opening, triage, booking, endCall). You will reuse them across every transition that leads to the node.
  • A node needs at least one of nodeId or name, and the platform derives the missing one. Send only a nodeId and the name is generated from it (verifyCustomer → “Verify Customer”, endCall → “End Call”); send only a name and the nodeId is generated from it in camelCase (“Verify Customer” → verifyCustomer). Send neither and the request is rejected.
A name-only node gets an auto-generated nodeId (the camelCase of its name, made unique). Because a transition’s toNodeId must match that id, any node another node points to should carry an explicit nodeId, it is the robust path and you still get the name for free. Deriving the id from the name is most convenient for nodes nothing transitions to (post-call actions, hand-off sub-actions).

Transition IDs: you do NOT provide them

A transition only needs two things from you: You never send a transition ID. The platform generates it from the transition name in camelCase (“is successful” becomes isSuccessful), adding a numeric suffix on collisions (isSuccessful1, isSuccessful2). This is why the transition name must be unique inside a node.
Write transition names as readable conditions, not code. Use “Age greater than 60”, not {customerAge} > 60.
A single transition:

The opening

Every flow needs an entry point. In most flows that is a single OpeningSentence node (nodeType: 2).
The opening script is spoken as written. Whatever you put in an OpeningSentence node’s script is what the agent says to open the conversation, close to verbatim. This is different from a Dialogue node, where the script is a guide that the agent adapts in real time to what the customer says.

The entry point is derived, not sent

You do not tell the API which node is first. The entry point is resolved automatically:
  • If the flow contains a PreCallAPI node, that node is the entry point.
  • Otherwise, the single OpeningSentence node is the entry point.
Because of this, the opening rules are strict:
  • Without a PreCallAPI: exactly one OpeningSentence node.
  • With a PreCallAPI: one or two OpeningSentence nodes (see the next section).

Pre-call API

A PreCallAPI node (nodeType: 3) runs an API call before the conversation starts, typically to look the customer up so the opening can be personalized. A flow may contain at most one PreCallAPI node. Its transitions are special: each one must set apiResult, and each one must lead to an OpeningSentence node.
apiResult is only valid on the transitions of a PreCallAPI node, and it is required there. It must not appear on any other node’s transitions.
One opening (continue on both outcomes): the Success transition leads to the opening. A Failure transition, if present, must point at that same opening. Two openings (branch on the outcome): the Success transition leads to the “known customer” opening, the Failure transition to the “unknown customer” opening. They must point at two different OpeningSentence nodes.
Here method: 1 is a GET (see the method codes under Action nodes). outputBody maps a field of the API response onto a flow variable so the opening can use {firstName}.

Connecting nodes into a sequence

A flow is only valid when it is fully connected. The rules:
  • Exactly one EndCall node (nodeType: 100), and every path must be able to reach it.
  • Every non-entry node needs at least one incoming transition. A node nobody points to is unreachable and rejected. (The one exception is a floating TransferCall node, see below.)
  • Transitions may only point at node IDs that exist in the flow.
A simple linear chain looks like this (order in the array does not matter, the toNodeId links do the wiring):
Give the EndCall node a stable ID like endCall and point every terminal branch at it. It keeps the graph easy to reason about, and every action node (transfer, SMS, email) should ultimately continue to it.

Action nodes

Action nodes do work instead of talking. Each carries the settings object that matches its nodeType. apiSettings (method codes: 1 GET, 2 POST, 3 PUT, 4 DELETE, 5 PATCH):
Action node settings on a node graph carry no trigger description: the transitions decide when an action runs, so there is nothing to describe. (The one exception is a floating TransferCall, see below.) For the full API body, headers, credentials, and output-schema options, see the API node guide and Flow Variables.

TransferCall: wired vs floating (Voice)

A TransferCall node behaves differently depending on how it is reached, and this changes whether triggerDescription is required:
A TransferCall node does not need an outgoing transition. Once the call is handed off there is nothing left to branch on, so the platform connects it straight to the EndCall node on its own. This holds whether the transfer is wired (it has an incoming transition) or floating (it has none): in both cases you can leave its transitions empty and the post-call flow still runs. The { "name": "Continue", "toNodeId": "endCall" } transition shown above is therefore optional.

Post-call actions

Post-call actions run after the conversation ends. They are fire-and-forget: the platform sends them and moves on, so they never have transitions and their result is not captured. The structure has two parts:
  1. A PostCallActions container node (nodeType: 200) whose postCallActions array holds the actual action nodes (API 40, Email 51, and on Voice also SMS 50). These inner nodes have no transitions.
  2. The EndCall node’s transitions, which point at those containers. This is the only place EndCall may transition to. Each transition name is the condition under which that container fires.
When the call ends, the platform evaluates all of EndCall’s transitions at once. Each condition that is true triggers its container, and multiple containers can fire in parallel (logical OR). Use a transition named “Always” for a container that should run on every call.
Post-call actions are the only place where post-call variables (post_call_summary, post_call_transcript, post_call_recording, …) are available. See Flow Variables.

Voice vs Text

The node logic above is identical for both channels. The differences are the node types allowed, the model, and a few channel fields.

The Hand-Off node (Text)

The Text equivalent of a transfer. It hands the conversation to a human and can fire inline notifications through its handOffAction array. Those sub-actions may be SMS (50), Email (51), or API (40), have no transitions of their own, and the Hand-Off node itself transitions to EndCall.
script on a Hand-Off is the last line the agent sends before it stops replying. Leave it empty for a silent hand-off.
See the Hand-Off node guide for the full behavior.

Creating vs updating

Create

Send the whole thing at once. The request wrapper is the same shape for both channels (only the settings variant differs):
  • Provide exactly one channel: inbound or outbound, never both.
  • For a Text Pearl, put the textChannelId on the inbound/outbound settings.
  • variables defines the flow variables; keep it minimal and only include what you reference. See Flow Variables.
  • The Pearl is created already published: the configuration you send becomes its live version.

Update: pearl is a full replace, settings are a patch

The update endpoints (Update Voice Pearl, Update Text Pearl) treat each part of the request differently. In every case the update applies to the published version of the Pearl: if several versions exist on the platform, only the currently published one is modified.
There is no partial edit of a single node. To change one node you resend the entire pearl.nodes graph. Fetch the current graph first with Get Pearl Settings, modify it, and send it back.
The channel patch works field by field: sending { "inbound": { "waitingSentence": "..." } } changes only the waiting sentence and preserves everything else. This is why inbound/outbound behave as a patch while pearl does not.

Validation checklist

Before you send a flow, confirm:
  • Exactly one EndCall node, and every node can reach it.
  • Exactly one OpeningSentence (or, with a PreCallAPI, one or two).
  • At most one PreCallAPI; its transitions all set apiResult and lead to OpeningSentence nodes.
  • Every nodeId is unique and every toNodeId points at an existing node.
  • Every non-entry node has an incoming transition (except a floating TransferCall).
  • Each node carries the settings object for its nodeType, and no forbidden node type for the channel.
  • apiResult only on PreCallAPI transitions; post-call and hand-off sub-actions have no transitions.
  • Transition names are unique within their node and written as plain-language conditions.

Flow Variables

The {variableId} syntax, variable groups, and built-in variables.

API Node

Request body, headers, credentials, and response output schema.

Get Pearl Settings

Read the current flow back before updating it.

Hand-Off Node

Escalate a Text conversation to a human.