# 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](https://voice.sphoro.com/docs/functions) |
| A second call tomorrow if this one failed | A [journey](https://voice.sphoro.com/docs/journeys) |

## 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

`POST /v1/workflows`

**Shell**

```bash
curl -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 |
| --- | --- | --- |
| `name` required | string | What you call it. |
| `steps` required | array | Run in order. Each has an `id`, a `type` and an `input`. |
| `id` required | string | On a step. Names it, and is how later steps address what it produced. |
| `type` required | string | On a step. One of the six above. |
| `input` optional | 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

`POST /v1/workflows/{id}/runs`

**Shell**

```bash
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": {
      "to_number": "+919876543210",
      "email": "priya@example.com",
      "appointment_start": "2026-09-15T10:30:00+05:30",
      "service": "cleaning",
      "outcome": "booked"
    }
  }'
```

`GET /v1/workflows/{id}/runs`

**Shell**

```bash
curl -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](https://voice.sphoro.com/docs/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](https://voice.sphoro.com/docs/journeys). |

**Shell**

```bash
# 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"}}'
```

> **Use [extractions](https://voice.sphoro.com/docs/post-call) as the input.** The whole point of typed extraction fields is that they are the workflow's arguments: `outcome`, `amount_agreed`, `callback_number`. A workflow driven off a summary string is a workflow parsing prose.

## Editing and removing

`GET /v1/workflows`

`PATCH /v1/workflows/{id}`

`DELETE /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

> **The calendar, CRM, mailer and query catalogue are configured on your account rather than in the workflow.** A step says "book an appointment"; where that booking lands is a connector Sphoro Voice sets up with you. That is why `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](https://voice.sphoro.com/docs/support) about the rest.
