Workflows
The work that happens because of a conversation, after it. Book the slot, update the CRM, send the confirmation, call your webhook. Six step types, run in order, with each step able to read what the ones before it produced.
When to use one
A workflow is the smallest of the three automation tools, and usually the right one.
| You want | Reach for |
|---|---|
| Something done after this call | A workflow |
| Something done during the call, spoken back to the caller | A function |
| A second call tomorrow if this one failed | A journey |
The six step types
| Type | Does |
|---|---|
book_appointment | Reserves a slot on a calendar, and can offer alternatives when the requested one is taken. |
update_crm | Writes the outcome onto a contact record. |
send_email | Sends one email — the confirmation, the summary, the follow-up. |
trigger_webhook | One HTTP request to your own systems. The escape hatch: anything not covered by the five others. |
query_db | Runs one of your account's named queries. A step names a query; it never supplies SQL. |
set | Computes a value into the run's data, for later steps to read. |
Creating one
/v1/workflowscurl -s -X POST https://voice.sphoro.com/v1/workflows \
-H "Authorization: Bearer $SPHORO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "After a booking call",
"steps": [
{ "id": "slot", "type": "book_appointment",
"input": {"start": "{{appointment_start}}", "service": "{{service}}"} },
{ "id": "crm", "type": "update_crm",
"input": {"phone": "{{to_number}}", "fields": {"last_outcome": "{{outcome}}"}} },
{ "id": "mail", "type": "send_email",
"input": {"to": "{{email}}", "subject": "Your appointment is confirmed",
"text": "See you on {{appointment_start}}."} }
]
}'| Field | Type | Description |
|---|---|---|
namerequired | string | What you call it. |
stepsrequired | array | Run in order. Each has an
id, a type and an input. |
idrequired | string | On a step. Names it, and is how later steps address what it produced. |
typerequired | string | On a step. One of the six above. |
inputoptional | object | On a step. Rendered against the run's data, so
{{…}} reads the input the run started with and anything earlier steps
produced. |
Running one
/v1/workflows/{id}/runscurl -s -X POST https://voice.sphoro.com/v1/workflows/$WORKFLOW_ID/runs \
-H "Authorization: Bearer $SPHORO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"to_number": "+919876543210",
"email": "priya@example.com",
"appointment_start": "2026-09-15T10:30:00+05:30",
"service": "cleaning",
"outcome": "booked"
}
}'/v1/workflows/{id}/runscurl -s https://voice.sphoro.com/v1/workflows/$WORKFLOW_ID/runs \
-H "Authorization: Bearer $SPHORO_API_KEY"workflow.run.completed and workflow.run.failed fire when a run ends —
see webhooks. A run carries its per-step results, so a failure names
the step rather than the workflow.
Wiring it to a finished call
Two ways, and which you want depends on whether the call is part of a longer sequence.
| Way | How |
|---|---|
| From your own webhook handler | Subscribe to call.analysed, then
POST /v1/workflows/{id}/runs with the extracted fields as the input. Explicit, and
you decide which calls qualify. |
| From inside a journey | An actions node names the workflow by id and
passes the execution's data as its input. See journeys. |
# From a call.analysed handler.
curl -s -X POST https://voice.sphoro.com/v1/workflows/$WORKFLOW_ID/runs \
-H "Authorization: Bearer $SPHORO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input": {"outcome": "booked", "to_number": "+919876543210"}}'outcome, amount_agreed, callback_number. A
workflow driven off a summary string is a workflow parsing prose.Editing and removing
/v1/workflows/v1/workflows/{id}/v1/workflows/{id}Steps replace whole on a patch, like every other list in this API — read, change, send the whole array back.
What the connectors are wired to
query_db names a query rather than carrying SQL — the statement is defined once,
against your database, and a workflow can only choose among the ones that exist.If a connector you need is not wired up, trigger_webhook gets you there today: it
posts to any endpoint of yours, and your side does the work. Talk to
us about the rest.