ApiWorkflows

Create a workflow

POST
/workflows

Create a workflow. The definition is validated on write: invalid definitions are rejected with HTTP 400 and a details array listing every problem (unknown node references, unreachable nodes, missing edge descriptions, and so on), so a workflow that saves is a workflow that runs. Attach it to a phone number with PATCH /numbers/{id}.

Authorization

BearerAuth
AuthorizationBearer <token>

API key authentication. Get your key from the dashboard.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

curl -X POST "https://example.com/workflows" \  -H "Content-Type: application/json" \  -d '{    "name": "Dental practice reception",    "definition": {      "version": 1,      "entry_node": "reception",      "global_prompt": "You work for Riverside Dental. Be warm and concise.",      "nodes": [        {          "id": "reception",          "type": "conversation",          "instructions": "You are the receptionist. Handle general questions. When the caller asks about availability, hand off to the availability check.",          "first_line": "Good afternoon, Riverside Dental. How can I help you?",          "global": true,          "global_description": "The caller wants the reception back or has a general question."        },        {          "id": "check_availability",          "type": "tool",          "tool_name": "check_availability",          "url": "https://api.example.com/availability",          "arguments": {            "location": "riverside",            "days_ahead": 7          },          "first_line": "One moment while I look that up for you."        },        {          "id": "specialist",          "type": "conversation",          "instructions": "You are Sam, the scheduling specialist. Use the availability result from the context to help the caller pick a slot.",          "first_line": "You are speaking with Sam, the scheduling specialist.",          "llm_config": {            "provider": "openai",            "model": "gpt-5.4-mini"          }        },        {          "id": "human",          "type": "transfer",          "destination": "+31201234567",          "first_line": "I am transferring you to a colleague now, one moment please."        },        {          "id": "goodbye",          "type": "end",          "first_line": "Thanks for calling Riverside Dental. Have a great day!"        }      ],      "edges": [        {          "from": "reception",          "to": "check_availability",          "description": "The caller asks about available appointment slots.",          "message": "Let me check that for you."        },        {          "from": "check_availability",          "to": "specialist"        },        {          "from": "specialist",          "to": "human",          "description": "The caller explicitly asks for a human employee."        },        {          "from": "reception",          "to": "goodbye",          "description": "The caller indicates the conversation is finished."        },        {          "from": "specialist",          "to": "goodbye",          "description": "The caller indicates the conversation is finished."        }      ]    }  }'
{
  "workflow": {
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "name": "Dental practice reception",
    "is_active": true,
    "definition": {
      "version": 1,
      "entry_node": "reception",
      "global_prompt": "You work for Riverside Dental. Be warm and concise.",
      "nodes": [
        {
          "id": "reception",
          "type": "conversation",
          "instructions": "You are the receptionist. Handle general questions. When the caller asks about availability, hand off to the availability check.",
          "first_line": "Good afternoon, Riverside Dental. How can I help you?",
          "global": true,
          "global_description": "The caller wants the reception back or has a general question."
        },
        {
          "id": "check_availability",
          "type": "tool",
          "tool_name": "check_availability",
          "url": "https://api.example.com/availability",
          "arguments": {
            "location": "riverside",
            "days_ahead": 7
          },
          "first_line": "One moment while I look that up for you."
        },
        {
          "id": "specialist",
          "type": "conversation",
          "instructions": "You are Sam, the scheduling specialist. Use the availability result from the context to help the caller pick a slot.",
          "first_line": "You are speaking with Sam, the scheduling specialist.",
          "llm_config": {
            "provider": "openai",
            "model": "gpt-5.4-mini"
          }
        },
        {
          "id": "human",
          "type": "transfer",
          "destination": "+31201234567",
          "first_line": "I am transferring you to a colleague now, one moment please."
        },
        {
          "id": "goodbye",
          "type": "end",
          "first_line": "Thanks for calling Riverside Dental. Have a great day!"
        }
      ],
      "edges": [
        {
          "from": "reception",
          "to": "check_availability",
          "description": "The caller asks about available appointment slots.",
          "message": "Let me check that for you."
        },
        {
          "from": "check_availability",
          "to": "specialist"
        },
        {
          "from": "specialist",
          "to": "human",
          "description": "The caller explicitly asks for a human employee."
        },
        {
          "from": "reception",
          "to": "goodbye",
          "description": "The caller indicates the conversation is finished."
        },
        {
          "from": "specialist",
          "to": "goodbye",
          "description": "The caller indicates the conversation is finished."
        }
      ]
    },
    "created_at": "2019-08-24T14:15:22Z",
    "updated_at": "2019-08-24T14:15:22Z"
  }
}
{
  "error": "Invalid workflow definition",
  "details": [
    "entry_node \"missing\" does not reference an existing node",
    "node 'human': transfer nodes require destination in E.164 format (e.g. +31612345678)"
  ]
}
{
  "error": "Invalid API key",
  "message": "string"
}