Features

Workflows

Build multi-step call flows on a visual canvas: conversation steps, background tool calls, human transfers and clean endings — with one assistant supplying the voice and call settings.

Workflows turn a single assistant prompt into a structured, multi-step call flow. Instead of one long system prompt that has to cover every situation, you draw the conversation as a graph: a reception step, a lookup step, a specialist step, a transfer to a human, a polite goodbye. Each step has its own focused instructions, and the assistant moves between steps based on transition rules you define.

Workflows are thoroughly tested and ready to use. They carry a Beta label until they have proven themselves at high call volumes and in rare edge cases.

When to use a workflow

A single well-written assistant handles most calls fine. Reach for a workflow when:

  • The call has distinct phases — intake, verification, scheduling, hand-off — and one prompt trying to do all of them gets muddled.
  • You need a guaranteed action mid-call — look something up, register a lead — without trusting the model to remember to call a tool.
  • Different steps need different behaviour — a cheap fast model for small talk, a stronger model for the step that does the real work.
  • The ending matters — always transfer to a human on request, always close the call politely.

Building blocks

Node typeWhat it doesTerminal
ConversationTalks with the caller under its own instructionsNo
ToolCalls your webhook in the background and advances automaticallyNo
TransferAnnounces and executes a blind transfer to a phone numberYes
EndSpeaks a goodbye line and hangs upYes

Edges connect nodes. Each edge carries a description — a plain-language condition such as "The caller asks about available appointment slots" — that tells the assistant when to take it, and an optional message spoken during the hand-over. Transitions are exclusive: one transition at a time, and the conversation context carries over so the caller never repeats themselves.

Global nodes are conversation nodes reachable from anywhere without drawing edges. Mark a node as global (for example the reception) and describe when a caller should land there — the platform wires the transition into every other conversation step automatically.

Global prompt. Shared context — company name, tone of voice, language — goes in the workflow's global prompt, which is prepended to every conversation node. Node instructions stay short and specific.

Deterministic by design

The engine executes the things that must happen in code, not in the prompt:

  • Tool nodes call your webhook themselves and always advance along their single outgoing edge — even when the call fails, the error lands in the conversation context so the next step can respond to it. No dead air, no forgotten lookups.
  • Transfer nodes speak their announcement once and then dial. The transfer is started by the platform the moment the node becomes active.
  • End nodes finish the goodbye line cleanly before hanging up.

The model decides when to move between steps; the platform guarantees what happens inside them.

The fixed lines of a tool node and a transfer node reach the caller differently on the two kinds of assistant. On a pipeline assistant the platform speaks them itself through text-to-speech, word for word. A realtime (speech-to-speech) assistant has no text-to-speech step to hand a line to, so there the platform writes your line into the step's instructions, opens a turn and waits for the model to speak. What the caller hears is the model's rendering of your line, in the caller's language when that differs from the language you wrote it in: read it as the wording you intend, not as the wording that will come out. If the model produces nothing, or the bounded wait runs out, the platform logs a warning on our side and continues: the tool node still calls your webhook, the transfer node still dials. See Tool node and Transfer node.

The workflow and your assistant

A workflow does not replace your assistant — it drives it. When a call comes in on a number with a workflow attached:

Comes from the workflowStays with the assistant
System prompt per stepDefault voice and speech models
Greeting (entry node's first line)Silence handling, max duration
Tools per stepVoicemail detection, recording consent
Optional model, voice, TTS and transcriber overrides per stepWebhooks, notifications, GDPR mode
Post-call analysis and reports
How the assistant speaks (speech_config)

One call, one transcript, one end-of-call report — exactly as without a workflow. This also means workflows work with every assistant configuration mode, including assistants resolved dynamically through the assistant-request webhook.

speech_config stays with the assistant, in full. All six speech settings — turn-taking, pronunciation, background audio, silence, end_call_message and first_message_outbound — are read from the assistant on every call and are not part of the workflow definition. A speech_config block on a node is refused when you save the workflow, with a 400 naming the node: a workflow overrides which model, voice, text-to-speech and transcriber a step runs on, not how the assistant handles turns, silence or its closing line. A definition saved before that check existed can still carry the field. It is ignored for the whole call, and logged on our side as a warning, because refusing to answer the phone over a field that never did anything would be the worse outcome.

One combination is worth knowing about. On an assistant with end_call_message set, the assistant's fixed closing line wins: an End node then hangs up without speaking its own first_line, so the caller hears one goodbye and not two. If you want the end node's wording, leave end_call_message off that assistant. (Before the September 2026 release both were spoken, one after the other. end_call_message is pipeline-only, so this never applied to a realtime assistant.) first_message_outbound never applies while a workflow runs, because workflows drive inbound calls only.

Workflows drive inbound phone calls. Outbound calls placed from the same number and web calls run the assistant's normal behaviour — attaching a workflow never changes those.

Per-step models, voices and transcribers. Every conversation step can override parts of the assistant. Overrides are checked twice, and both checks refuse rather than guess:

  • Model (llm_config): a different text model per step — a cheap fast model for small talk, a stronger one for the step that does the real work. Pipeline assistants only.
  • Voice (voice): a different voice per step, within the assistant's provider — on realtime assistants the speech-to-speech voice, on pipeline assistants the text-to-speech voice. A receptionist and a specialist can genuinely sound like different people.
  • Text-to-speech (tts_config) and speech recognition (stt_config): full per-step provider overrides including parameters — for example domain key terms or another language on one step. Pipeline assistants only; a realtime model speaks and listens itself.

When you save the workflow, the values in those blocks are checked against the platform catalog: an unknown provider or model, an unknown recognition language, an unknown voice for a provider whose voices we enumerate, and a realtime model set per step are all refused right there with a 400 naming the node and the field.

What that check cannot see is the assistant, because a workflow is not bound to one until you attach it to a number. Everything that depends on the assistant is therefore checked again at call start, fail-closed: a per-step model override, tts_config or stt_config on a realtime assistant, a voice that does not belong to the assistant's provider, and a config block with no provider in it all reject the call before pickup, never mid-conversation.

Creating a workflow

Build visually in the dashboard under Workflows — the canvas supports drag-and-drop nodes, auto-layout, keyboard shortcuts and undo/redo — or create one via the API:

curl -X POST https://api.hmsovereign.com/api/v1/workflows \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Dental practice reception",
    "definition": {
      "version": 1,
      "entry_node": "reception",
      "nodes": [
        {
          "id": "reception",
          "type": "conversation",
          "instructions": "You are the receptionist. Handle general questions.",
          "first_line": "Good afternoon, Riverside Dental. How can I help you?"
        },
        { "id": "goodbye", "type": "end", "first_line": "Thanks for calling!" }
      ],
      "edges": [
        {
          "from": "reception",
          "to": "goodbye",
          "description": "The caller indicates the conversation is finished."
        }
      ]
    }
  }'

Definitions are validated on write: an invalid graph — an unreachable node, a missing transition description, a transfer without a valid number — is rejected with a list of specific errors, and so is a per-step override the catalog does not recognise. What write-time validation cannot judge is the assistant, so a 201 says the definition is sound, not that every override suits the assistant that will end up running it; that part rejects at call start, as described above. The full field reference lives in Workflow definitions.

Attaching it to a number

A workflow answers calls once it is attached to a phone number — in the number's settings in the dashboard, or via the API:

curl -X PATCH https://api.hmsovereign.com/api/v1/numbers/NUMBER_ID \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "workflow_id": "WORKFLOW_UUID" }'

Good to know:

  • Pause without detaching. Set the workflow's is_active to false and attached numbers fall back to their assistant's normal behaviour immediately.
  • Deletion is protected. A workflow that is still attached to numbers cannot be deleted (the API returns 409), so a live number never changes behaviour as a side effect.
  • Transfers need a trunk. If the workflow contains a transfer node, the number needs a SIP trunk for transfers, same as the transfer tool.
  • Transfer nodes transfer blind. They dial and bridge immediately. Waiting for an answer, a ring timeout and backup numbers are features of the transfer tool, not of the transfer node — for no-answer handling, give a conversation node a transfer_call tool instead. See Transfer node.

API reference

On this page