Skip to content

Write the answering policy a verified BYO number answers by

PUT
/v1/phone-numbers/{number_id}/answering-policy
curl --request PUT \
--url https://api.vocapable.com/v1/phone-numbers/example/answering-policy \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "timezone": "example", "business_hours": { "additionalProperty": [ { "start": "example", "end": "example" } ] }, "holiday_dates": [ "2026-04-15" ], "open_steps": [ { "type": "menu", "prompt": "example", "branches": {}, "timeout_seconds": 5, "max_invalid_attempts": 2, "timeout_branch": [ { "type": "ring_humans", "rep_ids": [ "example" ], "timeout_seconds": 20 } ] } ], "closed_steps": [ { "type": "menu", "prompt": "example", "branches": {}, "timeout_seconds": 5, "max_invalid_attempts": 2, "timeout_branch": [ { "type": "ring_humans", "rep_ids": [ "example" ], "timeout_seconds": 20 } ] } ] }'

Replace one number’s whole answering document and put it on the policy route.

The same eligibility a live-agent route requires, for the same reason: a policy arms carrier ingress just as an agent does. What it additionally proves is that every reference inside the document resolves now - each ai step’s agent has a published immutable version, and every ring_humans rep is an active transfer-enabled rep of this tenant. Those are the two ways a policy could otherwise be written that answers a real caller with a dead end, and a carrier callback is the wrong place to discover either.

The document is written before the route is moved, so a number is never in policy mode with nothing to execute; both statements share the request transaction, so a failure at either point leaves the number on the route it already had.

number_id
required
Number Id
string
Media typeapplication/json
AnsweringPolicyUpsertRequest

The whole answering document for one number, written in a single call.

Whole-document rather than patch: the steps form a tree whose branches reference each other by position, and a partial write is the shape in which a menu keeps pointing at a branch the same request deleted.

business_hours is the tenant’s own calendar in the tenant’s own zone, and it shares only the vocabulary of campaign_schedules.calling_windows, never its meaning: the compliance layer’s quiet hours are keyed to the callee’s jurisdiction and are untouched here. An empty map means the number is answered by open_steps at every hour; naming any day at all makes every unnamed day closed, which is the same load-bearing empty-versus-absent distinction CallingSchedule.windows_for carries.

object
timezone
required
Timezone
string
>= 1 characters <= 64 characters
business_hours
Business Hours
object
key
additional properties
Array<object>
<= 6 items
CallingWindow

One local-time window on one weekday.

object
start
required
Start
string
/^([01]\d|2[0-3]):[0-5]\d$/
end
required
End
string
/^([01]\d|2[0-3]):[0-5]\d$/
holiday_dates
Holiday Dates
Array<string>
<= 60 items
open_steps
required
Open Steps
Array
>= 1 items <= 60 items
One of: discriminator: type
MenuStep

Read options, then follow the branch the caller pressed.

timeout_branch is required rather than optional, and it is also where the invalid budget lands once it is spent: a menu whose silent or fumbling caller has nowhere to go re-prompts forever, which is the single most common way a keypress tree becomes a trap.

object
type
Type
string
default: menu
Allowed value: menu
prompt
required
Prompt
string
>= 1 characters <= 1000 characters
branches
required
Branches
object
>= 1 properties <= 12 properties
timeout_seconds
Timeout Seconds
integer
default: 5 >= 1 <= 30
max_invalid_attempts
Max Invalid Attempts
integer
default: 2 >= 1 <= 5
timeout_branch
required
Timeout Branch
Array
>= 1 items <= 12 items
One of: discriminator: type
object recursive
closed_steps
Closed Steps
Array
<= 60 items
One of: discriminator: type
MenuStep

Read options, then follow the branch the caller pressed.

timeout_branch is required rather than optional, and it is also where the invalid budget lands once it is spent: a menu whose silent or fumbling caller has nowhere to go re-prompts forever, which is the single most common way a keypress tree becomes a trap.

object
type
Type
string
default: menu
Allowed value: menu
prompt
required
Prompt
string
>= 1 characters <= 1000 characters
branches
required
Branches
object
>= 1 properties <= 12 properties
timeout_seconds
Timeout Seconds
integer
default: 5 >= 1 <= 30
max_invalid_attempts
Max Invalid Attempts
integer
default: 2 >= 1 <= 5
timeout_branch
required
Timeout Branch
Array
>= 1 items <= 12 items
One of: discriminator: type
object recursive

Successful Response

Media typeapplication/json
AnsweringPolicy

One number’s stored answering document, as its tenant reads it back.

version is server-owned and bumped on every write. It is not an optimistic-locking token for this endpoint: it exists so a signed callback URL can carry the exact document revision it was minted against, and a caller walking a policy that was edited mid-call is detected rather than silently re-routed.

object
id
required
Id
string
phone_number_id
required
Phone Number Id
string
timezone
required
Timezone
string
business_hours
Business Hours
object
key
additional properties
Array<object>
CallingWindow

One local-time window on one weekday.

object
start
required
Start
string
/^([01]\d|2[0-3]):[0-5]\d$/
end
required
End
string
/^([01]\d|2[0-3]):[0-5]\d$/
holiday_dates
Holiday Dates
Array<string>
open_steps
required
Open Steps
Array
One of: discriminator: type
MenuStep

Read options, then follow the branch the caller pressed.

timeout_branch is required rather than optional, and it is also where the invalid budget lands once it is spent: a menu whose silent or fumbling caller has nowhere to go re-prompts forever, which is the single most common way a keypress tree becomes a trap.

object
type
Type
string
default: menu
Allowed value: menu
prompt
required
Prompt
string
>= 1 characters <= 1000 characters
branches
required
Branches
object
>= 1 properties <= 12 properties
timeout_seconds
Timeout Seconds
integer
default: 5 >= 1 <= 30
max_invalid_attempts
Max Invalid Attempts
integer
default: 2 >= 1 <= 5
timeout_branch
required
Timeout Branch
Array
>= 1 items <= 12 items
One of: discriminator: type
object recursive
closed_steps
Closed Steps
Array
One of: discriminator: type
MenuStep

Read options, then follow the branch the caller pressed.

timeout_branch is required rather than optional, and it is also where the invalid budget lands once it is spent: a menu whose silent or fumbling caller has nowhere to go re-prompts forever, which is the single most common way a keypress tree becomes a trap.

object
type
Type
string
default: menu
Allowed value: menu
prompt
required
Prompt
string
>= 1 characters <= 1000 characters
branches
required
Branches
object
>= 1 properties <= 12 properties
timeout_seconds
Timeout Seconds
integer
default: 5 >= 1 <= 30
max_invalid_attempts
Max Invalid Attempts
integer
default: 2 >= 1 <= 5
timeout_branch
required
Timeout Branch
Array
>= 1 items <= 12 items
One of: discriminator: type
object recursive
version
required
Version
integer
created_at
required
Created At
string format: date-time
updated_at
required
Updated At
string format: date-time
Example
{
"open_steps": [
{
"type": "menu",
"timeout_seconds": 5,
"max_invalid_attempts": 2,
"timeout_branch": [
{
"type": "ring_humans",
"timeout_seconds": 20
}
]
}
],
"closed_steps": [
{
"type": "menu",
"timeout_seconds": 5,
"max_invalid_attempts": 2,
"timeout_branch": [
{
"type": "ring_humans",
"timeout_seconds": 20
}
]
}
]
}

Validation Error

Media typeapplication/json
HTTPValidationError
object
detail
Detail
Array<object>
ValidationError
object
loc
required
Location
Array
msg
required
Message
string
type
required
Error Type
string
input
Input
ctx
Context
object
Examplegenerated
{
"detail": [
{
"loc": [
"example"
],
"msg": "example",
"type": "example",
"input": "example",
"ctx": {}
}
]
}