Creating a Voice Agent

This document provides a comprehensive guide for developers using the Partner API to create voice agents for inbound and outbound phone calls. It covers all prerequisites, endpoints, and the complete workflow from setup to agent creation.

Overview

A voice agent in the Partner API is a conversational AI that can handle phone calls. Creating and activating one involves:

  1. Prerequisites: Business ID, optional integrations (Google Calendar)

  2. Tools: Transfer tools, Google Calendar scheduling tools, or custom API tools

  3. Knowledge Base: Optional files for RAG (Retrieval-Augmented Generation)

  4. Voice Selection: Choose from available voices

  5. Agent Creation: Define the agent with workflow or prose, activated hours, and configuration

  6. Deployment: Deploy the agent to provision a phone number and make it live

The nexus for agent creation is the endpoint

POST /v1/businesses/:businessId/voice-agents

. After creation, deploy the agent using

POST /v1/businesses/:businessId/voice-agents/:voiceAgentId/deploy

to provision a phone number and activate it.

Once deployed:

  • INBOUND agents can receive calls at the provisioned phone number

  • OUTBOUND agents can make calls programmatically via the API

Prerequisites

Before creating a phone agent, ensure you have:

  1. Partner API Access: Valid API credentials with appropriate scopes (/v1/oauth/token endpoint)

  2. Business ID: A business entity that belongs to your organization

  3. Required Scopes:

    • voice-agents:create, voice-agents:read, voice-agents:update, voice-agents:delete - For creating, reading, updating, and deleting agents

    • tools:read, tools:create, tools:update, tools:delete - For managing tools

    • files:read, files:create, files:delete - For knowledge base files

    • voices:read - For selecting voices

    • integrations:read, integrations:create, integrations:update, integrations:delete - For Google Calendar (if needed)

    • calls:read - For viewing call history

Step-by-Step Guide

Step 1: Set Up Google Calendar Integration (Optional)

If you want your agent to schedule appointments in a Google Calendar, you must set up Google Calendar integration first.

Endpoint: POST /v1/businesses/:businessId/integrations/google-calendar

Request Body:

{
  "accessToken": "ya29.a0AfH6SMC...",  // Google OAuth access token
  "calendarId": "primary"               // Google Calendar ID (or specific calendar ID)
}

Response:

{
  "id": "config-id-123",
  "calendarId": "primary",
  "aclRuleId": "acl-rule-id-456"
}

What Happens:

  • The system shares the specified Google Calendar with a service account

  • Creates or updates a BusinessIntegrationConfig record

  • Returns calendarId and aclRuleId needed for Google Calendar tools

Important Notes:

  • The accessToken must have the https://www.googleapis.com/auth/calendar scope

  • The calendar must be successfully shared with the service account

  • This integration is required before creating Google Calendar tools

Example:

curl -X POST \
  https://api.example.com/partner-api/v1/businesses/biz_123/integrations/google-calendar \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "accessToken": "ya29.a0AfH6SMC...",
    "calendarId": "primary"
  }'

Verify Integration:

GET /v1/businesses/:businessId/integrations/google-calendar

Returns the same structure if configured, or 404 if not set up.

Step 2: Create Tools

Tools enable your agent to perform actions like transferring calls or scheduling appointments. Tools are created as instances from templates.

2.1: List Available Tool Templates from the Tool Library

Endpoint:

GET /v1/tools

Response:

{
  "items": [
    {
      "id": "template-transfer-001",
      "isTemplate": true,
      "name": "transferCall",
      "displayName": "Transfer Call",
      "type": "transferCall",
      "config": { /* template config */ },
      "configSchema": { /* JSON schema for configOverride */ }
    },
    {
      "id": "template-googlecal-001",
      "isTemplate": true,
      "name": "googleCal",
      "displayName": "Google Calendar",
      "type": "apiRequest",
      "config": { /* template config */ },
      "configSchema": { /* JSON schema for configOverride */ }
    }
  ],
  "page": 1,
  "page_size": 10,
  "total": 2
}

2.2: Create a Transfer Call Tool

A transfer call tool is used to transfer a call to a destination. The configuration options are:

  • Destinations: An array of destination objects. Each destination object has the following properties:

    • name: The name of the destination.

    • phoneNumber: The phone number of the destination.

    • description: The description of the destination.

    • warmTransferEnabled: Whether to verify availability before connecting to the destination.

  • Custom on hold audio: An optional audio URL to play while on hold. If not provided, the default on hold audio will be used.

    • This is specified in the messages array.

      • type: "request-complete"

      • content: The URL of the audio file to play while on hold.

Endpoint:

POST /v1/businesses/:businessId/tools

Request Body:

{
  "templateToolId": "template-transfer-001",
  "configOverride": {
    "destinations": [
      {
        "name": "Sales Team",
        "phoneNumber": "+15551234567",
        "description": "Main sales line",
        "warmTransferEnabled": true
      },
      {
        "name": "Support",
        "phoneNumber": "+15559876543",
        "description": "Customer support",
        "warmTransferEnabled": false
      }
    ],
    "messages": [
      {
        "type": "request-complete",
        "content": "https://example.com/audio/on-hold.mp3"
      }
    ]
  }
}

Response:

{
  "id": "tool-instance-123",
  "isTemplate": false,
  "templateToolId": "template-transfer-001",
  "name": "transferCall",
  "displayName": "Transfer Call",
  "type": "transferCall",
  "config": { /* base template config */ },
  "configOverride": { /* your overrides */ },
  "effectiveConfig": { /* merged config */ },
  "message": "Let me transfer you to our team...",
  "businessId": "biz_123",
  "enabled": true,
  "createdAt": "2024-01-15T10:00:00Z",
  "updatedAt": "2024-01-15T10:00:00Z"
}

Validation Rules:

  • Phone numbers must be in E.164 format: +1XXXXXXXXXX (US only)

  • Area code and exchange code cannot start with 0 or 1

  • At least one destination is required

  • messages array is optional (for on-hold audio)

What Happens:

  • Creates TransferDestination records in the database

  • Stores destinationIds in the tool's configOverride

  • Returns the tool instance ID (use this in toolIds when creating the agent)

Important: Save the returned

id

field (not

name

) for use in

toolIds

array. The

name

field is for your internal reference only.

2.3: Create a Google Calendar Tool

A Google Calendar tool is used to schedule appointments, search for appointments, and cancel appointments in a Google Calendar. The configuration options are:

  • Appointment config (optional): An object with the following properties:

    • slotDurationMinutes: The duration of each appointment slot in minutes. (default: 60)

    • slotBufferMinutes: The buffer time before and after each appointment slot in minutes. (default: 0)

    • advanceNoticeMinutes: The minimum number of minutes before the appointment time to allow for booking. (default: 1440)

    • hoursJson: Specify when appointments are available for booking with an object with the following properties: (defaults to business hours)

      • day of week mapped to an array of objects with the following properties:

        • openTime: The opening time of the day in HH:MM format.

        • closeTime: The closing time of the day in HH:MM format.

Prerequisite: Google Calendar integration must be set up (Step 1).

Endpoint:

POST /v1/businesses/:businessId/tools

Request Body:

{
  "templateToolId": "template-googlecal-001",
  "configOverride": {
    "appointmentConfig": {
      "slotDurationMinutes": 60,
      "slotBufferMinutes": 15,
      "advanceNoticeMinutes": 1440,
      "hoursJson": {
        "monday": [
          { "openTime": "09:00", "closeTime": "17:00" }
        ],
        "tuesday": [
          { "openTime": "09:00", "closeTime": "17:00" }
        ],
        "wednesday": [
          { "openTime": "09:00", "closeTime": "17:00" }
        ],
        "thursday": [
          { "openTime": "09:00", "closeTime": "17:00" }
        ],
        "friday": [
          { "openTime": "09:00", "closeTime": "17:00" }
        ]
      }
    }
  }
}

Response:

{
  "id": "tool-instance-456",
  "isTemplate": false,
  "templateToolId": "template-googlecal-001",
  "name": "googleCal",
  "displayName": "Google Calendar",
  "type": "apiRequest",
  "config": { /* base template config */ },
  "configOverride": {
    "appointmentConfigId": "appt-config-789"
  },
  "effectiveConfig": { /* merged config */ },
  "businessId": "biz_123",
  "enabled": true,
  "createdAt": "2024-01-15T10:05:00Z",
  "updatedAt": "2024-01-15T10:05:00Z"
}

Validation Rules:

  • slotDurationMinutes: 10-300 (default: 60)

  • slotBufferMinutes: 0-120 (default: 0)

  • advanceNoticeMinutes: 0-14400 (default: 1440 = 24 hours)

  • hoursJson: Optional, maps day names to arrays of {openTime, closeTime}

What Happens:

  • Validates Google Calendar integration exists for the business

  • Creates or updates an AppointmentConfig record

  • Stores appointmentConfigId in the tool's configOverride

  • The scheduling URL is constructed server-side from businessId when building the voice assistant

Important: Save the returned

id

field (not

name

) for use in

toolIds

array. The

name

field is auto-generated for internal reference only.

Note: The Google Calendar tool actually three tools in the platform:

  • scheduleAppointment - Book an appointment

  • searchAppointments - Search available slots

  • cancelAppointment - Cancel an appointment

All three are automatically added when you include the Google Calendar tool instance ID in

toolIds

.

2.3.1: Google Calendar Tools Details

The Google Calendar tool is a multi-tool - one tool instance creates three distinct tools in the platform. An agent that can schedule appointments can also look up and confirm existing appointments, cancel existing appointments (and therefore "reschedule" appointments by canceling and rebooking).

The Three Tools:

  1. scheduleAppointment - Books new appointments

    • Used when the caller wants to book a time slot

    • Agent will search for available slots and book the appointment

    • Example: "I'd like to schedule an appointment for next Tuesday"

  2. searchAppointments - Searches available time slots

    • Used to find existing appointments (uses fuzzy search on fields like phone number, customer name, and address to find the appointment)

    • Example: "Can you remind me of my appointment on Friday?"

  3. cancelAppointment - Cancels existing appointments

    • Used to cancel appointments that have already been booked

    • Example: "I need to cancel my appointment on Friday"

In addition to these three abilities, behind the scenes the integration will be used to provide the agent with a list of available times slots, when a call is initiated.

How They Work Together:

When you create a Google Calendar tool instance and include it in

toolIds

, all three tools become available to the agent. The agent will automatically use the appropriate tool based on the conversation:

  • If caller wants to book → uses scheduleAppointment (in combination with the available times slots)

  • If caller asks about an existing appointment → uses searchAppointments

  • If caller wants to cancel → uses searchAppointments, then cancelAppointment

  • If caller wants to reschedule → uses searchAppointments, then cancelAppointment then scheduleAppointment

Troubleshooting Google Calendar Tools:

  • "Google Calendar integration not configured"

    • Ensure you've set up the integration first (Step 1)

    • Verify with GET /v1/businesses/:businessId/integrations/google-calendar

  • "Appointment booking fails"

    • Check that appointmentConfig has valid hours

    • Verify slotDurationMinutes is reasonable (10-300)

    • Ensure advanceNoticeMinutes allows bookings for desired timeframes

  • "No available slots found"

    • Check hoursJson configuration

    • Ensure calendar actually has free time slots

2.4: List Your Business Tools

Endpoint:

GET /v1/businesses/:businessId/tools

Returns all tool instances (not templates) for the business. Use the returned IDs in

toolIds

when creating the agent. Always use tool IDs (not names) in the

toolIds

array when creating agents

Understanding Default Tools

When you create a voice agent, certain tools are automatically added even if you don't specify them in

toolIds

:

  • endCall tool is always present - you cannot disable it

  • a default transferCall tool is added if the business has a phone number configured. If you provide a custom transfer tool, the default is not added.

  • knowledgeBaseQuery tool is automatically created when you provide knowledgeBaseDocumentIds

Example Response:

{
  "toolIds": [
    "tool-instance-456",           // Your custom Google Calendar tool
    "auto-knowledge-base-tool",    // Auto-created from knowledgeBaseDocumentIds
    "auto-end-call-tool",          // Always auto-added
    "auto-transfer-tool"           // Auto-added (if no custom transfer provided)
  ]
}

Tool Library Reference

The following table shows all available tool types in the Partner API (these have

isTemplate: true

in the response):

Tool Type

Template Name

Purpose

Requires Config?

Auto-Added?

transferCall

transferCall

Transfer calls to destinations

Yes (destinations)

Conditional

googleCal

googleCal

Schedule/search/cancel appointments

Yes (Google Calendar integration)

No

endCall

endCall

End calls

No

Always

knowledgeBaseQuery

N/A

Query knowledge base

Auto-created from files

Conditional

function

function

Custom function calls

Yes (server URL, function name)

No

apiRequest

apiRequest

Generic API requests

Yes (URL, method)

No

These tool templates are then configured for use with agents in the relevant business.

Notes:

  • Tool templates are read-only and define the base configuration

  • Tool instances are created from templates with business-specific overrides

  • Use tool instance IDs (not template IDs) in toolIds when creating agents

Step 3: Upload Files for Knowledge Base (Optional)

Files are uploaded to a vector store for use in a knowledge base that an agent can query during conversations.

Endpoint: POST /v1/businesses/:businessId/files

Request:

multipart/form-data

Form Fields:

  • file: The file to upload (PDF, TXT, DOCX, etc.)

Response:

{
  "id": "file-123",
  "name": "product-catalog.pdf",
  "mimeType": "application/pdf",
  "sizeBytes": 1048576,
  "businessId": "biz_123",
  "status": "processing",
  "createdAt": "2024-01-15T10:10:00Z",
  "updatedAt": "2024-01-15T10:10:00Z"
}

Note: The

name

field in the response corresponds to the file's

displayName

in the database.

Status Values:

  • "processing" - File is being processed

  • "ready" - File is ready to use (both vector store processing completed)

  • "failed" - File processing failed

What Happens:

  1. File is uploaded to the vector store

  2. Record is added to the business's file table

  3. Status is tracked ("processing""ready")

Supported Formats:

  • PDF, TXT, DOCX, MD, and other text-based formats

Important Notes:

  • Files are processed asynchronously; status may be "processing" initially

  • Use the returned id in knowledgeBaseDocumentIds when creating the agent

N.B. Providing

knowledgeBaseDocumentIds

automatically creates a knowledge base query tool. This tool is added to the agent's

toolIds

in the response, but the tool ID is not predictable before agent creation. Ensure files have

status: "ready"

before using them in agent creation.

Monitoring File Processing

Files are processed asynchronously after upload. You can check the file status before using files in agent creation.

Check File Status:

Endpoint:

GET /v1/businesses/:businessId/files/:fileId

Response:

{
  "id": "file-123",
  "name": "product-catalog.pdf",
  "mimeType": "application/pdf",
  "sizeBytes": 1048576,
  "businessId": "biz_123",
  "status": "processing", // <---or "ready" or "failed"
  "createdAt": "2024-01-15T10:10:00Z",
  "updatedAt": "2024-01-15T10:15:00Z"
}

Note: The

name

field in the response corresponds to the file's

displayName

in the database.

Status Values:

  • "processing" - File is being processed

  • "ready" - File is ready to use (processing completed)

  • "failed" - File processing failed

Important: Files must have

status: "ready"

before they can be used in agent creation.

Polling or Webhooks: You can poll the file status or use webhooks to be notified when the file is ready.

Step 4: Select a Voice

Voices are available from a curated collection. List available voices and select one by ID.

Endpoint: GET /v1/voices

Response:

[
  {
    "id": "voice-abc123",
    "name": "Sarah",
    "category": "conversational",
    "description": "Friendly and professional",
    "previewUrl": "https://..."
  },
  {
    "id": "voice-def456",
    "name": "Michael",
    "category": "narrative",
    "description": "Clear and authoritative",
    "previewUrl": "https://..."
  }
]

Get Specific Voice:

GET /v1/voices/:voiceId

Usage:

  • Use the id in the voiceId field when creating the agent

  • If omitted, a default voice is used

  • Voices are read-only (managed by the platform)

  • Voices can be sampled using the previewUrl field

Step 5: Create the Voice Agent

This is the most complex endpoint. It combines all the above components into a working phone agent.

Endpoint: POST /v1/businesses/:businessId/voice-agents

Note: The

businessId

is provided as a path parameter in the URL, not in the request body.

Request Body Structure

{
  // Required
  name: string;                    // Agent name (1-40 chars)
  type: "INBOUND" | "OUTBOUND";    // Agent type

  // Workflow Definition (one of these required)
  conversationFlow?: string;        // Prose description of conversation
  workflow?: {                     // OR structured workflow graph
    nodes: Array<{
      id: string;
      type: "conversation" | "tool";
      prompt: string;
      firstMessage?: string;
      label?: string;
    }>;
    edges: Array<{
      from: string;
      to: string;
      when: string;
    }>;
    startNode: string;
  };

  // Optional Configuration
  customInstructions?: string;     // Additional special instructions for the agent
  voiceId?: string;                // From Step 4
  toolIds?: string[];              // From Step 2
  knowledgeBaseDocumentIds?: string[]; // From Step 3

  // Personality
  personality?: {
    tone?: string; // e.g. "friendly", "professional"
    style?: string; // e.g. "conversational", "narrative"
    description?: string; // e.g. "A friendly and professional agent"
    useEmpathy?: boolean; // e.g. true
    useHumor?: boolean; // e.g. false
  };

  // Messages
  firstMessage?: string;           // Initial greeting (optional)

  // Features
  multilingual?: boolean; // only enable if you genuinely need to support multiple languages
  backgroundSound?: string; // 'office' for the default office sound, or a custom sound URL
  collectFields?: string[];        // What structured data to collect from the call (e.g., ["name", "phone", "email", "favorite_color"])

  // Activated Hours (INBOUND only)
  activatedHours?: {
    timezone: string;              // IANA timezone (e.g., "America/New_York") - this is the timezone of the business
    hours: Array<{
      day: string;                // "monday", "tuesday", etc.
      startTime: string;          // "HH:MM" or "CLOSED" - the start time of the day
      endTime: string;            // "HH:MM" or "CLOSED" - the end time of the day
    }>;
  };
}

Workflow vs. Conversation Flow

You must provide either

workflow

or

conversationFlow

(not both). No matter what you provide the other will be created automatically. If both are provided,

workflow

takes precedence. Unless you are developing a workflow editor UI (e.g. using react-flow), it is recommended that you use the conversation flow option.

Option 1: Conversation Flow (Prose) - this is a description of the conversation flow in natural language

{
  "conversationFlow": "Greet the caller warmly. Ask for their name and reason for calling. If they want to schedule, use the scheduling tool. If they need support, transfer to support."
}

Option 2: Workflow Graph (Structured) - this is a structured graph of the conversation flow

{
  "workflow": {
    "nodes": [
      {
        "id": "start",
        "type": "conversation",
        "prompt": "Greet the caller warmly and ask for their name",
        "firstMessage": "Hello! Thank you for calling. How can I help you today?",
        "label": "Greeting"
      },
      {
        "id": "collect-info",
        "type": "conversation",
        "prompt": "Ask for the caller's reason for calling and collect their contact information",
        "label": "Collect Information"
      },
      {
        "id": "schedule",
        "type": "tool",
        "prompt": "scheduleAppointment",
        "label": "Schedule Appointment"
      },
      {
        "id": "transfer",
        "type": "tool",
        "prompt": "transferCall",
        "label": "Transfer Call"
      },
      {
        "id": "end",
        "type": "tool",
        "prompt": "endCall",
        "label": "End Call"
      }
    ],
    "edges": [
      {
        "from": "start",
        "to": "collect-info",
        "when": "always"
      },
      {
        "from": "collect-info",
        "to": "schedule",
        "when": "if caller wants to schedule"
      },
      {
        "from": "collect-info",
        "to": "transfer",
        "when": "if caller needs support"
      },
      {
        "from": "schedule",
        "to": "end",
        "when": "after scheduling"
      },
      {
        "from": "transfer",
        "to": "end",
        "when": "after transfer"
      }
    ],
    "startNode": "start"
  }
}

Workflow Graph Details:

  • Nodes:

    • type: "conversation" - AI-driven conversation phase

    • type: "tool" - System action (must be: "scheduleAppointment", "transferCall", or "endCall")

  • Edges: Define transitions between nodes with when conditions

  • startNode: Must reference a node ID in the nodes array

Graphs and Flows:

  • If you provide conversationFlow, a workflow graph is also generated in the background (non-blocking)

  • If you provide workflow, prose for a conversation flow is also generated in the background (non-blocking)

  • The system stores both formats for flexibility

Activated Hours (INBOUND Only)

Important:

activatedHours

can only be set for

INBOUND

agents. OUTBOUND agents are not affected by activated hours.

{
  "activatedHours": {
    "timezone": "America/New_York",
    "hours": [
      {
        "day": "monday",
        "startTime": "09:00",
        "endTime": "17:00"
      },
      {
        "day": "tuesday",
        "startTime": "09:00",
        "endTime": "17:00"
      },
      {
        "day": "wednesday",
        "startTime": "09:00",
        "endTime": "17:00"
      },
      {
        "day": "thursday",
        "startTime": "09:00",
        "endTime": "17:00"
      },
      {
        "day": "friday",
        "startTime": "09:00",
        "endTime": "17:00"
      },
      {
        "day": "saturday",
        "startTime": "CLOSED",
        "endTime": "CLOSED"
      },
      {
        "day": "sunday",
        "startTime": "CLOSED",
        "endTime": "CLOSED"
      }
    ]
  }
}

Validation Rules:

  • timezone: Must be a valid IANA timezone identifier

  • day: Must be one of: monday, tuesday, wednesday, thursday, friday, saturday, sunday (case-insensitive)

  • startTime/endTime:

    • Format: "HH:MM" (24-hour format)

    • Or both "CLOSED" for closed days

    • endTime can be "24:00" (end of day)

  • Start time must be before end time

  • CLOSED entries are filtered out (not stored in DB)

Default: If omitted, agent is "always on" (00:00-24:00 all days, UTC).

Complete Example Request

{
  "name": "Customer Service Agent",
  "type": "INBOUND",
  "conversationFlow": "Greet the caller warmly. Ask for their name and reason for calling. If they want to schedule, use the scheduling tool. If they need support, transfer to support.",
  "customInstructions": "Never mention specific prices or costs. Never give a quote over the phone.",
  "voiceId": "voice-abc123",
  "toolIds": ["transfer-tool-instance-456","google-cal-tool-instance-123"],
  "knowledgeBaseDocumentIds": ["file-123", "file-456"],
  "personality": {
    "tone": "friendly",
    "style": "conversational",
    "useEmpathy": true
  },
  "firstMessage": "Hello! Thank you for calling Mario Brothers Plumbing. How can I assist you today?",
  "backgroundSound": "office",
  "collectFields": ["name", "phone", "email"],
  "activatedHours": {
    "timezone": "America/New_York",
    "hours": [
      { "day": "monday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "tuesday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "wednesday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "thursday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "friday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "saturday", "startTime": "CLOSED", "endTime": "CLOSED" },
      { "day": "sunday", "startTime": "CLOSED", "endTime": "CLOSED" }
    ]
  }
}

Response

{
  "id": "agent-789",
  "name": "Customer Service Agent",
  "type": "INBOUND",
  "businessId": "biz_123",
  "conversationFlow": "Greet the caller warmly. Ask for their name and reason for calling. If they want to schedule, use the scheduling tool. If they need support, transfer to support.",
  "customInstructions": "Never mention specific prices or costs. Never give a quote over the phone.",
  "voiceId": "voice-abc123",
  "firstMessage": "Hello! Thank you for calling Mario Brothers Plumbing. How can I assist you today?",
  "backgroundSound": "office",
  "collectFields": ["name", "phone", "email"],
  "personality": {
    "tone": "friendly",
    "style": "conversational",
    "useEmpathy": true,
    "useHumor": false
  },
  "toolIds": ["transfer-tool-instance-456", "google-cal-tool-instance-123", "knowledge-base-query-tool-345"],
  "knowledgeBaseDocumentIds": ["file-123", "file-456"],
  "activatedHours": {
    "timezone": "America/New_York",
    "hours": [
      { "day": "monday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "tuesday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "wednesday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "thursday", "startTime": "09:00", "endTime": "17:00" },
      { "day": "friday", "startTime": "09:00", "endTime": "17:00" }
    ]
  },
  "workflow": null, // this is processing in the background
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T10:30:00Z"
}

Note: When a voice agent is first created it is not yet deployed. So there is no

phoneNumber

or

onDuty

status included in the response. These fields only appear after the agent is deployed (see Step 6: Deploy the Voice Agent).

What Happens Behind the Scenes

  1. Validation:

    • Business exists and belongs to your organization

    • activatedHours is valid (if provided, must be INBOUND)

    • Either workflow or conversationFlow is provided

    • toolIds reference valid tool instances for this business

  2. Workflow Processing:

    • If workflow provided: Validates graph structure, converts to prose

    • If conversationFlow provided: Uses as-is, generates graph in background

    • Creates a Workflow record with both prose and graph

  3. Structured Output (if collectFields provided):

    • Creates a structured output for field extraction

    • Used to extract structured data from conversations

  4. Voice Assistant Generation:

    • Merges workflow, tools, knowledge base, voice, personality

    • Generates voice assistant configuration

    • Auto-added tools:

      • Knowledge base query tool (if knowledgeBaseDocumentIds provided)

      • endCall tool (always present - cannot be disabled)

      • Default transferCall tool (if no custom transfer tool provided AND business phone exists)

  5. Voice Agent:

    • Creates voice assistant

    • If this fails, the entire operation is rolled back

  6. Database Storage:

    • Creates Agent record and associated records for features, activated hours, etc.

Step 6: Deploy the Voice Agent

Important: Creating a voice agent does not make it live or provision a phone number. The agent exists in the system but cannot receive calls until it is deployed.

Endpoint: POST /v1/businesses/:businessId/voice-agents/:voiceAgentId/deploy

Prerequisites:

  • Agent must be created (Step 5)

Request: No body required (uses path parameters)

Response:

Returns the same agent object as

GET /voice-agents/:voiceAgentId

, but now includes deployment information:

{
  "id": "agent-789",
  "name": "Customer Service Agent",
  "type": "INBOUND",
  "businessId": "biz_123",
  // ... all other agent fields ...
  "phoneNumber": "+15551234567",  // ← Now present! Phone number provisioned during deploy
  "onDuty": true,                // ← Now present! Initially on-duty after deployment
  // ... rest of agent fields ...
}

Key Difference: After deployment, the response includes

phoneNumber

and

onDuty

fields that were not present in the initial creation response.

What Happens During Deployment:

  1. Creates Deployment Record:

    • Creates a new deployment record (or updates existing if redeploying)

  2. Provisions Phone Number:

    • Gets or provisions a live phone number for the business(matching business's area code when possible)

    • Stores telephony configuration in the database

  3. Wires Phone Number to Assistant:

    • Links the voice assistant to the provisioned phone number

    • Configures webhook endpoints for call events

    • Sets up fallback destination (business's main phone number)

  4. Activates Deployment:

    • Updates deployment status to ACTIVE and sets onDuty: true

    • Stores phone number in deployment record

    • Agent is now ready to receive or make calls

Important Notes:

  • Phone Number Reuse: If the business already has a provisioned phone number, it will be reused (not provisioned again)

  • Area Code Matching: The system attempts to match the phone number's area code to the business's phone number area code

Setting Agent On-Duty

After deployment, the agent is on-duty by default. To make it unavailable for calls:

Endpoint:

PATCH /v1/businesses/:businessId/voice-agents/:voiceAgentId/off

This sets

onDuty: false

, making the agent unavailable to receive calls (for INBOUND) or make calls (for OUTBOUND).

To set on-duty:

Endpoint:

PATCH /v1/businesses/:businessId/voice-agents/:voiceAgentId/on

This sets

onDuty: true

, making the agent available to receive calls (for INBOUND) or make calls (for OUTBOUND).

Note: For INBOUND agents,

activatedHours

determines when the agent is actually available. A background job will automatically toggle the agent on and off duty based on the configured activated hours.

Using Your Agent

After deployment, your agent is on-duty and ready to handle calls. How you interact with the agent depends on its type:

  • INBOUND Agents: Call the agent using the phone number provided in the deployment response. The agent will answer and handle the conversation based on your configured workflow.

  • OUTBOUND Agents: Trigger calls programmatically using the API endpoint below. The agent will call the specified phone number and handle the conversation.

Initiating Outbound Calls

For OUTBOUND agents, you can initiate calls programmatically using the following endpoint:

Endpoint: POST /v1/businesses/:businessId/voice-agents/:voiceAgentId/call

Prerequisites:

  • Agent must be created and deployed

  • Agent must be of type OUTBOUND

  • Agent must be on-duty (use PATCH /voice-agents/:voiceAgentId/on if needed)

Request Body:

{
  "phoneNumber": "+15551234567",  // Required: Phone number in +1XXXXXXXXXX format
  "name": "John Doe",             // Optional: Name of the person being called
  "leadId": "lead-123",           // Optional: Lead ID to generate context from
  "context": "Customer interested in plumbing services",  // Optional: Custom context (used if leadId not provided)
  "additionalInfo": "Mentioned they saw our ad on Google", // Optional: Additional information for the agent
  "imageUrls": [                  // Optional: Image URLs to send during the call; the agent will see them after the call is initiated
    "https://example.com/image1.jpg",
    "https://example.com/image2.jpg"
  ]
}

Response:

{
  "success": true,
  "callId": "call-record-456",          // Call ID (use this to track and look up the call in call history)
  "phoneNumber": "+15551234567",        // Phone number that was called
  "status": "initiated"
}

Request Body Fields:

  • phoneNumber (required): The phone number to call in E.164 format (+1XXXXXXXXXX). Must be a valid US phone number.

  • name (optional): The name of the person being called. Used by the agent during the conversation.

  • leadId (optional): If provided, the system will generate context from the lead record. This includes lead details, source, and any associated information.

  • context (optional): Custom context string for the agent. Used if leadId is not provided. If neither leadId nor context is provided, a default context is generated.

  • additionalInfo (optional): Additional information to pass to the agent. This is appended to the context.

  • imageUrls (optional): Array of image URLs to send during the call. Images are sent to the agent after the call is initiated.

Validation Rules:

  • Phone number must be in E.164 format: +1XXXXXXXXXX (US only)

  • Area code and exchange code cannot start with 0 or 1

  • Agent must be OUTBOUND type

  • Agent must be deployed

  • Agent must be on-duty

What Happens:

  1. Validation:

    • Validates agent exists, is OUTBOUND type, is deployed, and is on-duty

    • Validates and normalizes phone number format

    • If leadId provided, verifies lead exists

  2. Context Generation:

    • If leadId provided: Generates context from lead record (includes lead details, source, etc.)

    • If context provided: Uses the provided context string

    • If neither provided: Generates default context using business name

  3. Call Initiation:

    • Initiates the call to the specified phone number, and will start ringing the recipient's phone

    • Creates a call record for tracking

    • If images provided, sends them to the agent after call starts

  4. Response:

    • Returns call ID, phone number, and status

Example Request:

curl -X POST \
  https://app.nicheandleads.com/api/partner/v1/businesses/biz_123/voice-agents/agent-789/call \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumber": "+15551234567",
    "name": "John Doe",
    "leadId": "lead-123",
    "additionalInfo": "Customer requested a callback"
  }'

Example Response:

{
  "success": true,
  "callId": "call-record-456",
  "phoneNumber": "+15551234567",
  "status": "initiated"
}

Error Responses:

  • 400 BAD_REQUEST: Invalid phone number format, agent not OUTBOUND type, agent not deployed, or agent not on-duty

  • 404 NOT_FOUND: Agent not found or lead not found (if leadId provided)

  • 409 CONFLICT: Call record already exists (possible race condition with webhook)

  • 500 INTERNAL_ERROR: Failed to initiate call

Call History

After calls are made or received, you can retrieve call history using the following endpoints:

Business-Scoped Endpoints:

  • GET /v1/businesses/:businessId/calls - List calls for a business

  • GET /v1/businesses/:businessId/calls/:callId - Get a specific call

Organization-Scoped Endpoints:

  • GET /v1/organizations/calls - List calls for an organization (optionally filter by businessId)

  • GET /v1/organizations/calls/:callId - Get a specific call

Query Parameters (List Endpoints):

  • page (optional, default: 1): Page number

  • page_size (optional, default: 10, max: 100): Number of results per page

  • type (optional): Filter by call type - "INBOUND" or "OUTBOUND"

  • status (optional): Filter by call status - "SCHEDULED", "IN_PROGRESS", "COMPLETED", or "FAILED"

  • start (optional): Filter calls started after this date (ISO 8601 format)

  • end (optional): Filter calls started before this date (ISO 8601 format)

  • leadId (optional): Filter calls associated with a specific lead

  • orderBy (optional): Object with optional field (e.g., "startedAt", "endedAt", "durationMs") and optional direction ("asc" or "desc")

Response (List):

{
  "items": [
    {
      "id": "call-123",
      "businessId": "biz_123",
      "agentId": "agent-789",
      "leadId": "lead-456",
      "type": "OUTBOUND",
      "phoneNumber": "+15551234567",
      "customerNumber": "+15559876543",
      "status": "COMPLETED",
      "endedReason": "Call completed successfully",
      "summary": "Customer interested in scheduling a service appointment",
      "transcript": "Agent: Hello...",
      "recordingUrl": "https://...",
      "durationMs": 120000,
      "structuredData": { /* extracted data */ },
      "leadContext": {
        "context": "Customer context...",
        "generatedFromLeadId": "lead-456",
        "additionalInfo": "Additional info",
        "name": "John Doe",
        "imageUrls": ["https://..."]
      },
      "startedAt": "2024-01-15T10:00:00Z",
      "endedAt": "2024-01-15T10:02:00Z"
    }
  ],
  "total": 100,
  "page": 1,
  "page_size": 10
}

Response (Get by ID):

Returns a single call object with the same structure as items in the list response.

Example Request:

curl -X GET \
  "https://app.nicheandleads.com/api/partner/v1/businesses/biz_123/calls?page=1&page_size=10&status=COMPLETED&type=OUTBOUND" \
  -H "Authorization: Bearer YOUR_API_KEY"

Required Scope:

calls:read

API Reference

Base URL

https://app.nicheandleads.com/api/partner/v1

Authentication

All requests require an

Authorization

header (see OAuth):

Authorization: Bearer YOUR_API_KEY

Endpoints Summary

Method

Endpoint

Description

GET

/voices

List available voices

GET

/voices/:voiceId

Get specific voice

GET

/tools

List tool templates

GET

/tools/:toolId

Get tool template

GET

/businesses/:businessId/tools

List business tool instances

POST

/businesses/:businessId/tools

Create tool instance

GET

/businesses/:businessId/tools/:toolId

Get tool instance

PATCH

/businesses/:businessId/tools/:toolId

Update tool instance

DELETE

/businesses/:businessId/tools/:toolId

Delete tool instance

GET

/businesses/:businessId/files

List business files

POST

/businesses/:businessId/files

Upload file

GET

/businesses/:businessId/files/:fileId

Get file

DELETE

/businesses/:businessId/files/:fileId

Delete file

GET

/businesses/:businessId/integrations/google-calendar

Get Google Calendar integration

POST

/businesses/:businessId/integrations/google-calendar

Create Google Calendar integration

GET

/businesses/:businessId/voice-agents

List voice agents

POST

/businesses/:businessId/voice-agents

Create voice agent

GET

/businesses/:businessId/voice-agents/:voiceAgentId

Get voice agent

PATCH

/businesses/:businessId/voice-agents/:voiceAgentId

Update voice agent

DELETE

/businesses/:businessId/voice-agents/:voiceAgentId

Delete voice agent

POST

/businesses/:businessId/voice-agents/:voiceAgentId/deploy

Deploy voice agent (provisions phone number)

PATCH

/businesses/:businessId/voice-agents/:voiceAgentId/on

Set agent on-duty

PATCH

/businesses/:businessId/voice-agents/:voiceAgentId/off

Set agent off-duty

POST

/businesses/:businessId/voice-agents/:voiceAgentId/call

Initiate outbound call (outbound agents only)

GET

/businesses/:businessId/calls

List calls for a business

GET

/businesses/:businessId/calls/:callId

Get specific call for a business

GET

/organizations/calls

List calls for an organization

GET

/organizations/calls/:callId

Get specific call for an organization

Last updated: 2026-01-05