Dial is a communication stack for AI agents. Use these tools to send SMS, iMessage and WhatsApp messages, place AI-handled voice calls, manage phone numbers, and wait for inbound account events. iMessage: buy a number with purchase_number capabilities including "imessage"; its sends go out as iMessage by default, with native threaded replies, reactions, typing indicators, polls and group conversations. WhatsApp: a number with WhatsApp ready (list_numbers shows whatsapp.status "ready") sends WhatsApp when send_message is given channel "whatsapp", and supports reactions, quoted replies, voice notes and groups (create_group). Use lookup_number first to see whether a recipient can receive iMessage or WhatsApp. Tools that send from a number default to your primary number when fromNumber and fromNumberId are omitted; place_call is asynchronous — use wait_for_event (eventType call.ended) then get_call for the transcript.
Discoverability
2 of 6 checks33/100
Client listings and public setup information.
mcp.json on main domain
The manifest declares this MCP server's exact endpoint.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description99/100
Check whether the account is ready to send messages and place calls. Returns the account id and email, how many phone numbers it has, ready (true once it has at least one), and nextStep — what to do before sending, or null when ready. Call it first when you are unsure the account is set up.
The description explains the capability, when to use it, and what to expect.
Inputs100/100
This tool declares no inputs, so argument descriptions are not needed.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"accountId",
"email",
"numberCount",
"ready",
"nextStep"
],
"properties": {
"email": {
"type": "string",
"description": "Email address the account signed in with"
},
"ready": {
"type": "boolean",
"description": "True once the account has at least one number"
},
"nextStep": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "What to do next, or null when ready"
},
"accountId": {
"type": "string",
"description": "Dial account id"
},
"numberCount": {
"type": "number",
"description": "How many phone numbers are on the account"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "get_account_status",
"title": "Account Status",
"description": "Check whether the account is ready to send messages and place calls. Returns the account id and email, how many phone numbers it has, ready (true once it has at least one), and nextStep — what to do before sending, or null when ready. Call it first when you are unsure the account is set up.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"accountId",
"email",
"numberCount",
"ready",
"nextStep"
],
"properties": {
"email": {
"type": "string",
"description": "Email address the account signed in with"
},
"ready": {
"type": "boolean",
"description": "True once the account has at least one number"
},
"nextStep": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "What to do next, or null when ready"
},
"accountId": {
"type": "string",
"description": "Dial account id"
},
"numberCount": {
"type": "number",
"description": "How many phone numbers are on the account"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description94/100
List the phone numbers on your account. Each one carries its id, E.164 number and nickname, the channels it supports (capabilities), whether it is ready to use (setupStatus), its calling, forwarding and inbound-call settings, and its display identity. Pass an id or number from here as fromNumber, fromNumberId or numberId to other tools.
The description explains the capability, when to use it, and what to expect.
Inputs100/100
This tool declares no inputs, so argument descriptions are not needed.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"numbers"
],
"properties": {
"numbers": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"number"
],
"properties": {
"id": {
"type": "string",
"description": "Phone number id (pn_…)"
},
"number": {
"type": "string",
"description": "E.164 phone number"
},
"tenDlc": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"kind",
"submittedAt",
"reviewedAt",
"reason"
],
"properties": {
"kind": {
"anyOf": [
{
"enum": [
"sole_proprietor",
"business"
],
"type": "string"
},
{
"type": "null"
}
]
},
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why it was rejected; set only when status is 'rejected'"
},
"status": {
"enum": [
"not_registered",
"in_review",
"with_carrier",
"approved",
"rejected"
],
"type": "string"
},
"reviewedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
},
"submittedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "US 10DLC carrier registration. Until it is 'approved', carriers block outbound SMS/MMS from this number to US numbers (inbound and voice are unaffected). Null when 10DLC doesn't apply: a non-US number, an iMessage number, or an account that can't register one. Register in the dashboard."
},
"country": {
"type": "string",
"description": "ISO-3166-1 alpha-2 country"
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display last name; null on numbers without iMessage"
},
"nickname": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable label for the number"
},
"whatsapp": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"error",
"retryAvailableAt"
],
"properties": {
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when status is 'failed', 'banned' or 'paused'"
},
"status": {
"enum": [
"provisioning",
"warming_up",
"ready",
"failed",
"banned",
"paused"
],
"type": "string"
},
"retryAvailableAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601: when the channel will accept another verification attempt; null when it will now"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The WhatsApp channel's own setup state, independent of setupStatus, or null when the number has no WhatsApp registration. 'warming_up' means a number you connected is registered and is being warmed up for about 6 hours before first use. Sending with channel 'whatsapp' is refused until status is 'ready', and again once 'banned' — which means the channel was withdrawn from a line that was working, is permanent, and can only be recovered by registering WhatsApp on the number again. 'paused' is temporary: Dial has paused WhatsApp sending on the line, sends are refused with whatsapp_unavailable, and it returns to 'ready' on its own (each move is a number.status_changed event)."
},
"avatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's iMessage avatar photo; null when unset or not an iMessage number"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when the number was provisioned"
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display first name; null on numbers without iMessage"
},
"forwardTo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "E.164 number inbound calls are forwarded to instead of the AI voice agent answering, or null when the agent answers."
},
"setupError": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when setupStatus is 'failed'"
},
"setupStatus": {
"enum": [
"provisioning",
"ready",
"failed"
],
"type": "string",
"description": "Whether the number is ready to use; iMessage numbers are 'provisioning' until set up"
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"sms",
"call",
"imessage",
"whatsapp"
],
"type": "string"
},
"description": "Channels the number supports: [\"sms\",\"call\"], [\"sms\",\"call\",\"imessage\"], or that plus \"whatsapp\" once a WhatsApp registration on the line is live"
},
"whatsappName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "WhatsApp display name; null on numbers without a WhatsApp track"
},
"callingEnabled": {
"type": "boolean",
"description": "Whether calling is switched on for this number, both directions. false → inbound calls are not connected and place_call from this number fails with calling_disabled; messaging is unaffected. Separate from capabilities, which does not change when the switch is flipped: a number can place a call now only when capabilities contains \"call\", setupStatus is \"ready\", AND this is true"
},
"inboundLanguage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "BCP-47 language tag inbound calls are pinned to; null → detected from the caller's country prefix per call"
},
"inboundCallPolicy": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"name",
"scope"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The allow list screening inbound calls on this number right now (its own list, else the account-wide one), or null when every caller rings through. Callers not on it are declined. Always null on numbers that can't take calls."
},
"whatsappAvatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's WhatsApp avatar; null when unset"
},
"inboundInstruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "System prompt for inbound calls"
},
"inboundVoiceGender": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Voice gender for inbound calls (\"male\"/\"female\"); null → female (the default)"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "list_numbers",
"title": "List Numbers",
"description": "List the phone numbers on your account. Each one carries its id, E.164 number and nickname, the channels it supports (capabilities), whether it is ready to use (setupStatus), its calling, forwarding and inbound-call settings, and its display identity. Pass an id or number from here as fromNumber, fromNumberId or numberId to other tools.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"numbers"
],
"properties": {
"numbers": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"number"
],
"properties": {
"id": {
"type": "string",
"description": "Phone number id (pn_…)"
},
"number": {
"type": "string",
"description": "E.164 phone number"
},
"tenDlc": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"kind",
"submittedAt",
"reviewedAt",
"reason"
],
"properties": {
"kind": {
"anyOf": [
{
"enum": [
"sole_proprietor",
"business"
],
"type": "string"
},
{
"type": "null"
}
]
},
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why it was rejected; set only when status is 'rejected'"
},
"status": {
"enum": [
"not_registered",
"in_review",
"with_carrier",
"approved",
"rejected"
],
"type": "string"
},
"reviewedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
},
"submittedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "US 10DLC carrier registration. Until it is 'approved', carriers block outbound SMS/MMS from this number to US numbers (inbound and voice are unaffected). Null when 10DLC doesn't apply: a non-US number, an iMessage number, or an account that can't register one. Register in the dashboard."
},
"country": {
"type": "string",
"description": "ISO-3166-1 alpha-2 country"
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display last name; null on numbers without iMessage"
},
"nickname": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable label for the number"
},
"whatsapp": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"error",
"retryAvailableAt"
],
"properties": {
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when status is 'failed', 'banned' or 'paused'"
},
"status": {
"enum": [
"provisioning",
"warming_up",
"ready",
"failed",
"banned",
"paused"
],
"type": "string"
},
"retryAvailableAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601: when the channel will accept another verification attempt; null when it will now"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The WhatsApp channel's own setup state, independent of setupStatus, or null when the number has no WhatsApp registration. 'warming_up' means a number you connected is registered and is being warmed up for about 6 hours before first use. Sending with channel 'whatsapp' is refused until status is 'ready', and again once 'banned' — which means the channel was withdrawn from a line that was working, is permanent, and can only be recovered by registering WhatsApp on the number again. 'paused' is temporary: Dial has paused WhatsApp sending on the line, sends are refused with whatsapp_unavailable, and it returns to 'ready' on its own (each move is a number.status_changed event)."
},
"avatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's iMessage avatar photo; null when unset or not an iMessage number"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when the number was provisioned"
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display first name; null on numbers without iMessage"
},
"forwardTo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "E.164 number inbound calls are forwarded to instead of the AI voice agent answering, or null when the agent answers."
},
"setupError": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when setupStatus is 'failed'"
},
"setupStatus": {
"enum": [
"provisioning",
"ready",
"failed"
],
"type": "string",
"description": "Whether the number is ready to use; iMessage numbers are 'provisioning' until set up"
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"sms",
"call",
"imessage",
"whatsapp"
],
"type": "string"
},
"description": "Channels the number supports: [\"sms\",\"call\"], [\"sms\",\"call\",\"imessage\"], or that plus \"whatsapp\" once a WhatsApp registration on the line is live"
},
"whatsappName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "WhatsApp display name; null on numbers without a WhatsApp track"
},
"callingEnabled": {
"type": "boolean",
"description": "Whether calling is switched on for this number, both directions. false → inbound calls are not connected and place_call from this number fails with calling_disabled; messaging is unaffected. Separate from capabilities, which does not change when the switch is flipped: a number can place a call now only when capabilities contains \"call\", setupStatus is \"ready\", AND this is true"
},
"inboundLanguage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "BCP-47 language tag inbound calls are pinned to; null → detected from the caller's country prefix per call"
},
"inboundCallPolicy": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"name",
"scope"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The allow list screening inbound calls on this number right now (its own list, else the account-wide one), or null when every caller rings through. Callers not on it are declined. Always null on numbers that can't take calls."
},
"whatsappAvatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's WhatsApp avatar; null when unset"
},
"inboundInstruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "System prompt for inbound calls"
},
"inboundVoiceGender": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Voice gender for inbound calls (\"male\"/\"female\"); null → female (the default)"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Buy an additional phone number for your account. This spends money: the number is a recurring charge (on a pay-as-you-go account the first month is taken from the wallet right away), so call it only when the account holder asked for a new number, and record their consent in explicitProgrammaticConsent. Returns the new number with its id and setupStatus; standard and WhatsApp numbers are usable at once.
The description explains the capability, when to use it, and what to expect.
Inputs82/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
explicitProgrammaticConsent
A short, human-readable attestation that the account holder explicitly consented to provisioning this number programmatically (e.g. a reference to where/how they consented). Stored on the number for the provisioning audit trail.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
capabilities
Which channels the number carries. Omit for a standard SMS and voice number. Add imessage alongside sms and call for an iMessage number, whose calling finishes setting up asynchronously: poll list_numbers until setupStatus is "ready". Use sms with whatsapp (beta, enabled per account; whatsapp alone means the same) for an SMS and WhatsApp number without calls. It arrives with WhatsApp ready, and sends from it default to SMS, so pass channel "whatsapp" to send WhatsApp. When no WhatsApp number is available the purchase is refused with whatsapp_unavailable and nothing is charged, so retrying shortly is safe
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
inboundInstruction
What the AI voice agent should do when someone calls this number (its system prompt); Dial's default instruction is used when omitted
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
inboundVoiceGender
Whether the agent answering calls to this number speaks with a male or a female voice; female when omitted
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
inboundLanguage
Language calls to this number are answered in, as a BCP-47 tag such as fr-FR; omit to detect it from each caller's country prefix (with en-US alongside)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
areaCode
Preferred US area code, e.g. 415; omit for any available US number. Only US numbers can be provisioned at this time. Ignored for iMessage and WhatsApp-only numbers
Clear enough to supply this argument.
callingEnabled
Whether calling is switched on for the new number; on when omitted. Pass false for a messaging-only line: inbound calls are never connected and place_call from it fails, with no window in which it answers a call. Changeable later with set_number_properties.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"number"
],
"properties": {
"number": {
"type": "object",
"required": [
"id",
"number"
],
"properties": {
"id": {
"type": "string",
"description": "Phone number id (pn_…)"
},
"number": {
"type": "string",
"description": "E.164 phone number"
},
"tenDlc": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"kind",
"submittedAt",
"reviewedAt",
"reason"
],
"properties": {
"kind": {
"anyOf": [
{
"enum": [
"sole_proprietor",
"business"
],
"type": "string"
},
{
"type": "null"
}
]
},
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why it was rejected; set only when status is 'rejected'"
},
"status": {
"enum": [
"not_registered",
"in_review",
"with_carrier",
"approved",
"rejected"
],
"type": "string"
},
"reviewedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
},
"submittedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "US 10DLC carrier registration. Until it is 'approved', carriers block outbound SMS/MMS from this number to US numbers (inbound and voice are unaffected). Null when 10DLC doesn't apply: a non-US number, an iMessage number, or an account that can't register one. Register in the dashboard."
},
"country": {
"type": "string",
"description": "ISO-3166-1 alpha-2 country"
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display last name; null on numbers without iMessage"
},
"nickname": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable label for the number"
},
"whatsapp": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"error",
"retryAvailableAt"
],
"properties": {
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when status is 'failed', 'banned' or 'paused'"
},
"status": {
"enum": [
"provisioning",
"warming_up",
"ready",
"failed",
"banned",
"paused"
],
"type": "string"
},
"retryAvailableAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601: when the channel will accept another verification attempt; null when it will now"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The WhatsApp channel's own setup state, independent of setupStatus, or null when the number has no WhatsApp registration. 'warming_up' means a number you connected is registered and is being warmed up for about 6 hours before first use. Sending with channel 'whatsapp' is refused until status is 'ready', and again once 'banned' — which means the channel was withdrawn from a line that was working, is permanent, and can only be recovered by registering WhatsApp on the number again. 'paused' is temporary: Dial has paused WhatsApp sending on the line, sends are refused with whatsapp_unavailable, and it returns to 'ready' on its own (each move is a number.status_changed event)."
},
"avatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's iMessage avatar photo; null when unset or not an iMessage number"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when the number was provisioned"
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display first name; null on numbers without iMessage"
},
"forwardTo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "E.164 number inbound calls are forwarded to instead of the AI voice agent answering, or null when the agent answers."
},
"setupError": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when setupStatus is 'failed'"
},
"setupStatus": {
"enum": [
"provisioning",
"ready",
"failed"
],
"type": "string",
"description": "Whether the number is ready to use; iMessage numbers are 'provisioning' until set up"
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"sms",
"call",
"imessage",
"whatsapp"
],
"type": "string"
},
"description": "Channels the number supports: [\"sms\",\"call\"], [\"sms\",\"call\",\"imessage\"], or that plus \"whatsapp\" once a WhatsApp registration on the line is live"
},
"whatsappName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "WhatsApp display name; null on numbers without a WhatsApp track"
},
"callingEnabled": {
"type": "boolean",
"description": "Whether calling is switched on for this number, both directions. false → inbound calls are not connected and place_call from this number fails with calling_disabled; messaging is unaffected. Separate from capabilities, which does not change when the switch is flipped: a number can place a call now only when capabilities contains \"call\", setupStatus is \"ready\", AND this is true"
},
"inboundLanguage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "BCP-47 language tag inbound calls are pinned to; null → detected from the caller's country prefix per call"
},
"inboundCallPolicy": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"name",
"scope"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The allow list screening inbound calls on this number right now (its own list, else the account-wide one), or null when every caller rings through. Callers not on it are declined. Always null on numbers that can't take calls."
},
"whatsappAvatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's WhatsApp avatar; null when unset"
},
"inboundInstruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "System prompt for inbound calls"
},
"inboundVoiceGender": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Voice gender for inbound calls (\"male\"/\"female\"); null → female (the default)"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "purchase_number",
"title": "Purchase Number",
"description": "Buy an additional phone number for your account. This spends money: the number is a recurring charge (on a pay-as-you-go account the first month is taken from the wallet right away), so call it only when the account holder asked for a new number, and record their consent in explicitProgrammaticConsent. Returns the new number with its id and setupStatus; standard and WhatsApp numbers are usable at once.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"explicitProgrammaticConsent"
],
"properties": {
"areaCode": {
"type": "string",
"description": "Preferred US area code, e.g. 415; omit for any available US number. Only US numbers can be provisioned at this time. Ignored for iMessage and WhatsApp-only numbers"
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"sms",
"call",
"imessage",
"whatsapp"
],
"type": "string"
},
"description": "Which channels the number carries. Omit for a standard SMS and voice number. Add imessage alongside sms and call for an iMessage number, whose calling finishes setting up asynchronously: poll list_numbers until setupStatus is \"ready\". Use sms with whatsapp (beta, enabled per account; whatsapp alone means the same) for an SMS and WhatsApp number without calls. It arrives with WhatsApp ready, and sends from it default to SMS, so pass channel \"whatsapp\" to send WhatsApp. When no WhatsApp number is available the purchase is refused with whatsapp_unavailable and nothing is charged, so retrying shortly is safe"
},
"callingEnabled": {
"type": "boolean",
"description": "Whether calling is switched on for the new number; on when omitted. Pass false for a messaging-only line: inbound calls are never connected and place_call from it fails, with no window in which it answers a call. Changeable later with set_number_properties."
},
"inboundLanguage": {
"enum": [
"en-US",
"en-GB",
"en-AU",
"en-IN",
"en-NZ",
"de-DE",
"fr-FR",
"fr-CA",
"es-ES",
"es-419",
"it-IT",
"pt-BR",
"pt-PT",
"nl-NL",
"nl-BE",
"pl-PL",
"ru-RU",
"ro-RO",
"tr-TR",
"he-IL",
"ar-SA",
"hi-IN",
"kn-IN",
"ta-IN",
"ja-JP",
"zh-CN",
"ko-KR",
"vi-VN",
"th-TH",
"ms-MY",
"fil-PH"
],
"type": "string",
"description": "Language calls to this number are answered in, as a BCP-47 tag such as fr-FR; omit to detect it from each caller's country prefix (with en-US alongside)"
},
"inboundInstruction": {
"type": "string",
"minLength": 1,
"description": "What the AI voice agent should do when someone calls this number (its system prompt); Dial's default instruction is used when omitted"
},
"inboundVoiceGender": {
"enum": [
"male",
"female"
],
"type": "string",
"description": "Whether the agent answering calls to this number speaks with a male or a female voice; female when omitted"
},
"explicitProgrammaticConsent": {
"type": "string",
"maxLength": 2000,
"minLength": 1,
"description": "A short, human-readable attestation that the account holder explicitly consented to provisioning this number programmatically (e.g. a reference to where/how they consented). Stored on the number for the provisioning audit trail."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"number"
],
"properties": {
"number": {
"type": "object",
"required": [
"id",
"number"
],
"properties": {
"id": {
"type": "string",
"description": "Phone number id (pn_…)"
},
"number": {
"type": "string",
"description": "E.164 phone number"
},
"tenDlc": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"kind",
"submittedAt",
"reviewedAt",
"reason"
],
"properties": {
"kind": {
"anyOf": [
{
"enum": [
"sole_proprietor",
"business"
],
"type": "string"
},
{
"type": "null"
}
]
},
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why it was rejected; set only when status is 'rejected'"
},
"status": {
"enum": [
"not_registered",
"in_review",
"with_carrier",
"approved",
"rejected"
],
"type": "string"
},
"reviewedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
},
"submittedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "US 10DLC carrier registration. Until it is 'approved', carriers block outbound SMS/MMS from this number to US numbers (inbound and voice are unaffected). Null when 10DLC doesn't apply: a non-US number, an iMessage number, or an account that can't register one. Register in the dashboard."
},
"country": {
"type": "string",
"description": "ISO-3166-1 alpha-2 country"
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display last name; null on numbers without iMessage"
},
"nickname": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable label for the number"
},
"whatsapp": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"error",
"retryAvailableAt"
],
"properties": {
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when status is 'failed', 'banned' or 'paused'"
},
"status": {
"enum": [
"provisioning",
"warming_up",
"ready",
"failed",
"banned",
"paused"
],
"type": "string"
},
"retryAvailableAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601: when the channel will accept another verification attempt; null when it will now"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The WhatsApp channel's own setup state, independent of setupStatus, or null when the number has no WhatsApp registration. 'warming_up' means a number you connected is registered and is being warmed up for about 6 hours before first use. Sending with channel 'whatsapp' is refused until status is 'ready', and again once 'banned' — which means the channel was withdrawn from a line that was working, is permanent, and can only be recovered by registering WhatsApp on the number again. 'paused' is temporary: Dial has paused WhatsApp sending on the line, sends are refused with whatsapp_unavailable, and it returns to 'ready' on its own (each move is a number.status_changed event)."
},
"avatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's iMessage avatar photo; null when unset or not an iMessage number"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when the number was provisioned"
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display first name; null on numbers without iMessage"
},
"forwardTo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "E.164 number inbound calls are forwarded to instead of the AI voice agent answering, or null when the agent answers."
},
"setupError": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when setupStatus is 'failed'"
},
"setupStatus": {
"enum": [
"provisioning",
"ready",
"failed"
],
"type": "string",
"description": "Whether the number is ready to use; iMessage numbers are 'provisioning' until set up"
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"sms",
"call",
"imessage",
"whatsapp"
],
"type": "string"
},
"description": "Channels the number supports: [\"sms\",\"call\"], [\"sms\",\"call\",\"imessage\"], or that plus \"whatsapp\" once a WhatsApp registration on the line is live"
},
"whatsappName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "WhatsApp display name; null on numbers without a WhatsApp track"
},
"callingEnabled": {
"type": "boolean",
"description": "Whether calling is switched on for this number, both directions. false → inbound calls are not connected and place_call from this number fails with calling_disabled; messaging is unaffected. Separate from capabilities, which does not change when the switch is flipped: a number can place a call now only when capabilities contains \"call\", setupStatus is \"ready\", AND this is true"
},
"inboundLanguage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "BCP-47 language tag inbound calls are pinned to; null → detected from the caller's country prefix per call"
},
"inboundCallPolicy": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"name",
"scope"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The allow list screening inbound calls on this number right now (its own list, else the account-wide one), or null when every caller rings through. Callers not on it are declined. Always null on numbers that can't take calls."
},
"whatsappAvatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's WhatsApp avatar; null when unset"
},
"inboundInstruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "System prompt for inbound calls"
},
"inboundVoiceGender": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Voice gender for inbound calls (\"male\"/\"female\"); null → female (the default)"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description95/100
Update a number's properties: its inbound instruction (the system prompt its AI voice agent uses on calls to it), inbound voice gender, inbound language, nickname, whether calling is switched on at all (callingEnabled), where inbound calls are forwarded instead of the AI agent answering (forwardTo), and its display identity. For the display identity prefer `channel` ("imessage", "whatsapp", or "both") together with `name`/`avatarUrl` — one call that keeps every channel's profile identical. The per-channel fields remain for when the two profiles differ: firstName/lastName/avatarUrl for iMessage, whatsappName/whatsappAvatarUrl for WhatsApp-ready numbers. Provide at least one property, and don't mix `channel` with the per-channel fields. When an update targets both the iMessage and WhatsApp profiles, each channel succeeds or fails on its own: check `profileResults` in the result: each targeted channel is `updated` or `failed` (a failed channel is left unchanged; the rest of the update was saved). The call errors only when every targeted profile failed, and then nothing changed. Returns the number with its new settings.
Describe the returned information so the agent knows whether it will answer the request.
Inputs88/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
number
Which of your numbers to update, in E.164 form such as +14155550123 (see list_numbers)
Clear enough to supply this argument.
inboundInstruction
What the AI voice agent should do when someone calls this number (its system prompt); replaces the current one
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
inboundVoiceGender
Whether the agent answering calls to this number speaks with a male or a female voice; omitted leaves it unchanged. New numbers start female.
Clear enough to supply this argument.
inboundLanguage
Language calls to this number are answered in, as a BCP-47 tag such as fr-FR; omitted leaves it unchanged. When unset, the language is detected from the caller's country prefix on each call.
Clear enough to supply this argument.
nickname
A label of your own for the number, e.g. "Support line"; other tools accept it as fromNumber. Pass an empty string to clear it.
Clear enough to supply this argument.
maxCallDurationSeconds
Call duration cap for this number, in seconds, applied as a hard ceiling to both inbound and outbound calls (the smallest of the per-number, account, and per-call caps wins). On a free account (no top-up or subscription yet) a value above 300 is rejected with 400. Pass null to clear the cap; omit to leave it unchanged.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
channel
Which profile(s) `name` and `avatarUrl` are written to. Use "both" when the number should present the same identity everywhere — it is one call instead of two. Each channel succeeds or fails on its own: a value one channel rejects (e.g. a WhatsApp-incompatible photo, or a WhatsApp line that is not connected) fails only that channel, the rest is saved, and `profileResults` in the result says which channel was updated and which failed. Cannot be combined with firstName/lastName/whatsappName/whatsappAvatarUrl.
Clear enough to supply this argument.
name
The display name to set on the channel(s) `channel` names; requires `channel`. WhatsApp stores it verbatim; iMessage has no single-name field, so it is split on the first space — "Ana Lee" becomes firstName "Ana", lastName "Lee".
Clear enough to supply this argument.
firstName
iMessage display first name shown beside this number's messages in recipients' Messages apps. iMessage numbers only. Pass an empty string to clear it.
Clear enough to supply this argument.
lastName
Last name shown beside this number's messages in recipients' Messages apps. Applies to iMessage numbers only. Pass an empty string to clear it.
Clear enough to supply this argument.
avatarUrl
Link to a publicly reachable image to use as the number's profile photo; Dial downloads it. Without `channel` this is the iMessage avatar (jpeg/png/gif/webp, max 5 MB, iMessage numbers only). With `channel` it is applied to the channel(s) named there; WhatsApp's rules are stricter (square jpeg or png, 192x192 to 640x640, no transparent pixels), and a photo that misses them fails only the WhatsApp channel (reported as failed in `profileResults.whatsapp`) while iMessage still takes it. The photo can be replaced but not removed.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
whatsappName
WhatsApp display name shown to recipients, up to 25 characters and without reserved verification marks. WhatsApp-ready numbers only; the call blocks until WhatsApp applies it.
Resolve the mismatch between this description and its schema constraints.
whatsappAvatarUrl
Link to a publicly reachable image to use as the number's WhatsApp profile photo; Dial downloads it. It must be a square jpeg or png between 192x192 and 640x640 (not resized), with no transparent pixels. WhatsApp-ready numbers only.
Clear enough to supply this argument.
callingEnabled
Switch calling on or off for this number, in both directions. false stops inbound calls from being connected (the caller is never answered) and makes place_call from this number fail with calling_disabled; messaging on the number is unaffected. Takes effect on the next call — a call already in progress is not ended. Omitted leaves it unchanged.
Clear enough to supply this argument.
forwardTo
Forward inbound calls to this phone number (E.164) instead of having the AI voice agent answer; the caller is connected when that phone answers, and the call ends if it is busy or doesn't answer. null stops forwarding. Ignored while calling is off. Omitted leaves it unchanged.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"number"
],
"properties": {
"number": {
"type": "object",
"required": [
"id",
"number"
],
"properties": {
"id": {
"type": "string",
"description": "Phone number id (pn_…)"
},
"number": {
"type": "string",
"description": "E.164 phone number"
},
"tenDlc": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"kind",
"submittedAt",
"reviewedAt",
"reason"
],
"properties": {
"kind": {
"anyOf": [
{
"enum": [
"sole_proprietor",
"business"
],
"type": "string"
},
{
"type": "null"
}
]
},
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why it was rejected; set only when status is 'rejected'"
},
"status": {
"enum": [
"not_registered",
"in_review",
"with_carrier",
"approved",
"rejected"
],
"type": "string"
},
"reviewedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
},
"submittedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "US 10DLC carrier registration. Until it is 'approved', carriers block outbound SMS/MMS from this number to US numbers (inbound and voice are unaffected). Null when 10DLC doesn't apply: a non-US number, an iMessage number, or an account that can't register one. Register in the dashboard."
},
"country": {
"type": "string",
"description": "ISO-3166-1 alpha-2 country"
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display last name; null on numbers without iMessage"
},
"nickname": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable label for the number"
},
"whatsapp": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"error",
"retryAvailableAt"
],
"properties": {
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when status is 'failed', 'banned' or 'paused'"
},
"status": {
"enum": [
"provisioning",
"warming_up",
"ready",
"failed",
"banned",
"paused"
],
"type": "string"
},
"retryAvailableAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601: when the channel will accept another verification attempt; null when it will now"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The WhatsApp channel's own setup state, independent of setupStatus, or null when the number has no WhatsApp registration. 'warming_up' means a number you connected is registered and is being warmed up for about 6 hours before first use. Sending with channel 'whatsapp' is refused until status is 'ready', and again once 'banned' — which means the channel was withdrawn from a line that was working, is permanent, and can only be recovered by registering WhatsApp on the number again. 'paused' is temporary: Dial has paused WhatsApp sending on the line, sends are refused with whatsapp_unavailable, and it returns to 'ready' on its own (each move is a number.status_changed event)."
},
"avatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's iMessage avatar photo; null when unset or not an iMessage number"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when the number was provisioned"
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display first name; null on numbers without iMessage"
},
"forwardTo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "E.164 number inbound calls are forwarded to instead of the AI voice agent answering, or null when the agent answers."
},
"setupError": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when setupStatus is 'failed'"
},
"setupStatus": {
"enum": [
"provisioning",
"ready",
"failed"
],
"type": "string",
"description": "Whether the number is ready to use; iMessage numbers are 'provisioning' until set up"
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"sms",
"call",
"imessage",
"whatsapp"
],
"type": "string"
},
"description": "Channels the number supports: [\"sms\",\"call\"], [\"sms\",\"call\",\"imessage\"], or that plus \"whatsapp\" once a WhatsApp registration on the line is live"
},
"whatsappName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "WhatsApp display name; null on numbers without a WhatsApp track"
},
"callingEnabled": {
"type": "boolean",
"description": "Whether calling is switched on for this number, both directions. false → inbound calls are not connected and place_call from this number fails with calling_disabled; messaging is unaffected. Separate from capabilities, which does not change when the switch is flipped: a number can place a call now only when capabilities contains \"call\", setupStatus is \"ready\", AND this is true"
},
"inboundLanguage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "BCP-47 language tag inbound calls are pinned to; null → detected from the caller's country prefix per call"
},
"inboundCallPolicy": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"name",
"scope"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The allow list screening inbound calls on this number right now (its own list, else the account-wide one), or null when every caller rings through. Callers not on it are declined. Always null on numbers that can't take calls."
},
"whatsappAvatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's WhatsApp avatar; null when unset"
},
"inboundInstruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "System prompt for inbound calls"
},
"inboundVoiceGender": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Voice gender for inbound calls (\"male\"/\"female\"); null → female (the default)"
}
},
"additionalProperties": false
},
"profileResults": {
"type": "object",
"properties": {
"imessage": {
"type": "object",
"required": [
"status"
],
"properties": {
"code": {
"type": "string"
},
"error": {
"type": "string",
"description": "On a failure: plain-language reason the channel's profile wasn't updated"
},
"status": {
"enum": [
"updated",
"failed"
],
"type": "string",
"description": "What happened to this channel's profile"
},
"httpStatus": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "On a failure: the HTTP status it maps to (400 rejected value or channel not on the number; 5xx failure applying it)"
}
},
"additionalProperties": false
},
"whatsapp": {
"type": "object",
"required": [
"status"
],
"properties": {
"code": {
"type": "string"
},
"error": {
"type": "string",
"description": "On a failure: plain-language reason the channel's profile wasn't updated"
},
"status": {
"enum": [
"updated",
"failed"
],
"type": "string",
"description": "What happened to this channel's profile"
},
"httpStatus": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "On a failure: the HTTP status it maps to (400 rejected value or channel not on the number; 5xx failure applying it)"
}
},
"additionalProperties": false
}
},
"description": "Present whenever the update targeted a display profile: each targeted channel's outcome. A `failed` channel was left unchanged while the other channel and the rest of the update were saved. Fix the cause and retry just that channel.",
"additionalProperties": false
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "set_number_properties",
"title": "Set Number Properties",
"description": "Update a number's properties: its inbound instruction (the system prompt its AI voice agent uses on calls to it), inbound voice gender, inbound language, nickname, whether calling is switched on at all (callingEnabled), where inbound calls are forwarded instead of the AI agent answering (forwardTo), and its display identity. For the display identity prefer `channel` (\"imessage\", \"whatsapp\", or \"both\") together with `name`/`avatarUrl` — one call that keeps every channel's profile identical. The per-channel fields remain for when the two profiles differ: firstName/lastName/avatarUrl for iMessage, whatsappName/whatsappAvatarUrl for WhatsApp-ready numbers. Provide at least one property, and don't mix `channel` with the per-channel fields. When an update targets both the iMessage and WhatsApp profiles, each channel succeeds or fails on its own: check `profileResults` in the result: each targeted channel is `updated` or `failed` (a failed channel is left unchanged; the rest of the update was saved). The call errors only when every targeted profile failed, and then nothing changed. Returns the number with its new settings.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"number"
],
"properties": {
"name": {
"type": "string",
"minLength": 1,
"description": "The display name to set on the channel(s) `channel` names; requires `channel`. WhatsApp stores it verbatim; iMessage has no single-name field, so it is split on the first space — \"Ana Lee\" becomes firstName \"Ana\", lastName \"Lee\"."
},
"number": {
"type": "string",
"minLength": 7,
"description": "Which of your numbers to update, in E.164 form such as +14155550123 (see list_numbers)"
},
"channel": {
"enum": [
"imessage",
"whatsapp",
"both"
],
"type": "string",
"description": "Which profile(s) `name` and `avatarUrl` are written to. Use \"both\" when the number should present the same identity everywhere — it is one call instead of two. Each channel succeeds or fails on its own: a value one channel rejects (e.g. a WhatsApp-incompatible photo, or a WhatsApp line that is not connected) fails only that channel, the rest is saved, and `profileResults` in the result says which channel was updated and which failed. Cannot be combined with firstName/lastName/whatsappName/whatsappAvatarUrl."
},
"lastName": {
"type": "string",
"maxLength": 30,
"description": "Last name shown beside this number's messages in recipients' Messages apps. Applies to iMessage numbers only. Pass an empty string to clear it."
},
"nickname": {
"type": "string",
"maxLength": 100,
"description": "A label of your own for the number, e.g. \"Support line\"; other tools accept it as fromNumber. Pass an empty string to clear it."
},
"avatarUrl": {
"type": "string",
"format": "uri",
"description": "Link to a publicly reachable image to use as the number's profile photo; Dial downloads it. Without `channel` this is the iMessage avatar (jpeg/png/gif/webp, max 5 MB, iMessage numbers only). With `channel` it is applied to the channel(s) named there; WhatsApp's rules are stricter (square jpeg or png, 192x192 to 640x640, no transparent pixels), and a photo that misses them fails only the WhatsApp channel (reported as failed in `profileResults.whatsapp`) while iMessage still takes it. The photo can be replaced but not removed."
},
"firstName": {
"type": "string",
"maxLength": 30,
"description": "iMessage display first name shown beside this number's messages in recipients' Messages apps. iMessage numbers only. Pass an empty string to clear it."
},
"forwardTo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Forward inbound calls to this phone number (E.164) instead of having the AI voice agent answer; the caller is connected when that phone answers, and the call ends if it is busy or doesn't answer. null stops forwarding. Ignored while calling is off. Omitted leaves it unchanged."
},
"whatsappName": {
"type": "string",
"description": "WhatsApp display name shown to recipients, up to 25 characters and without reserved verification marks. WhatsApp-ready numbers only; the call blocks until WhatsApp applies it."
},
"callingEnabled": {
"type": "boolean",
"description": "Switch calling on or off for this number, in both directions. false stops inbound calls from being connected (the caller is never answered) and makes place_call from this number fail with calling_disabled; messaging on the number is unaffected. Takes effect on the next call — a call already in progress is not ended. Omitted leaves it unchanged."
},
"inboundLanguage": {
"enum": [
"en-US",
"en-GB",
"en-AU",
"en-IN",
"en-NZ",
"de-DE",
"fr-FR",
"fr-CA",
"es-ES",
"es-419",
"it-IT",
"pt-BR",
"pt-PT",
"nl-NL",
"nl-BE",
"pl-PL",
"ru-RU",
"ro-RO",
"tr-TR",
"he-IL",
"ar-SA",
"hi-IN",
"kn-IN",
"ta-IN",
"ja-JP",
"zh-CN",
"ko-KR",
"vi-VN",
"th-TH",
"ms-MY",
"fil-PH"
],
"type": "string",
"description": "Language calls to this number are answered in, as a BCP-47 tag such as fr-FR; omitted leaves it unchanged. When unset, the language is detected from the caller's country prefix on each call."
},
"whatsappAvatarUrl": {
"type": "string",
"format": "uri",
"description": "Link to a publicly reachable image to use as the number's WhatsApp profile photo; Dial downloads it. It must be a square jpeg or png between 192x192 and 640x640 (not resized), with no transparent pixels. WhatsApp-ready numbers only."
},
"inboundInstruction": {
"type": "string",
"minLength": 1,
"description": "What the AI voice agent should do when someone calls this number (its system prompt); replaces the current one"
},
"inboundVoiceGender": {
"enum": [
"male",
"female"
],
"type": "string",
"description": "Whether the agent answering calls to this number speaks with a male or a female voice; omitted leaves it unchanged. New numbers start female."
},
"maxCallDurationSeconds": {
"anyOf": [
{
"type": "integer",
"maximum": 3600,
"minimum": 60
},
{
"type": "null"
}
],
"description": "Call duration cap for this number, in seconds, applied as a hard ceiling to both inbound and outbound calls (the smallest of the per-number, account, and per-call caps wins). On a free account (no top-up or subscription yet) a value above 300 is rejected with 400. Pass null to clear the cap; omit to leave it unchanged."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"number"
],
"properties": {
"number": {
"type": "object",
"required": [
"id",
"number"
],
"properties": {
"id": {
"type": "string",
"description": "Phone number id (pn_…)"
},
"number": {
"type": "string",
"description": "E.164 phone number"
},
"tenDlc": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"kind",
"submittedAt",
"reviewedAt",
"reason"
],
"properties": {
"kind": {
"anyOf": [
{
"enum": [
"sole_proprietor",
"business"
],
"type": "string"
},
{
"type": "null"
}
]
},
"reason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why it was rejected; set only when status is 'rejected'"
},
"status": {
"enum": [
"not_registered",
"in_review",
"with_carrier",
"approved",
"rejected"
],
"type": "string"
},
"reviewedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
},
"submittedAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601 timestamp"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "US 10DLC carrier registration. Until it is 'approved', carriers block outbound SMS/MMS from this number to US numbers (inbound and voice are unaffected). Null when 10DLC doesn't apply: a non-US number, an iMessage number, or an account that can't register one. Register in the dashboard."
},
"country": {
"type": "string",
"description": "ISO-3166-1 alpha-2 country"
},
"lastName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display last name; null on numbers without iMessage"
},
"nickname": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable label for the number"
},
"whatsapp": {
"anyOf": [
{
"type": "object",
"required": [
"status",
"error",
"retryAvailableAt"
],
"properties": {
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when status is 'failed', 'banned' or 'paused'"
},
"status": {
"enum": [
"provisioning",
"warming_up",
"ready",
"failed",
"banned",
"paused"
],
"type": "string"
},
"retryAvailableAt": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "ISO-8601: when the channel will accept another verification attempt; null when it will now"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The WhatsApp channel's own setup state, independent of setupStatus, or null when the number has no WhatsApp registration. 'warming_up' means a number you connected is registered and is being warmed up for about 6 hours before first use. Sending with channel 'whatsapp' is refused until status is 'ready', and again once 'banned' — which means the channel was withdrawn from a line that was working, is permanent, and can only be recovered by registering WhatsApp on the number again. 'paused' is temporary: Dial has paused WhatsApp sending on the line, sends are refused with whatsapp_unavailable, and it returns to 'ready' on its own (each move is a number.status_changed event)."
},
"avatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's iMessage avatar photo; null when unset or not an iMessage number"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when the number was provisioned"
},
"firstName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "iMessage display first name; null on numbers without iMessage"
},
"forwardTo": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "E.164 number inbound calls are forwarded to instead of the AI voice agent answering, or null when the agent answers."
},
"setupError": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reason when setupStatus is 'failed'"
},
"setupStatus": {
"enum": [
"provisioning",
"ready",
"failed"
],
"type": "string",
"description": "Whether the number is ready to use; iMessage numbers are 'provisioning' until set up"
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"sms",
"call",
"imessage",
"whatsapp"
],
"type": "string"
},
"description": "Channels the number supports: [\"sms\",\"call\"], [\"sms\",\"call\",\"imessage\"], or that plus \"whatsapp\" once a WhatsApp registration on the line is live"
},
"whatsappName": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "WhatsApp display name; null on numbers without a WhatsApp track"
},
"callingEnabled": {
"type": "boolean",
"description": "Whether calling is switched on for this number, both directions. false → inbound calls are not connected and place_call from this number fails with calling_disabled; messaging is unaffected. Separate from capabilities, which does not change when the switch is flipped: a number can place a call now only when capabilities contains \"call\", setupStatus is \"ready\", AND this is true"
},
"inboundLanguage": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "BCP-47 language tag inbound calls are pinned to; null → detected from the caller's country prefix per call"
},
"inboundCallPolicy": {
"anyOf": [
{
"type": "object",
"required": [
"id",
"name",
"scope"
],
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "The allow list screening inbound calls on this number right now (its own list, else the account-wide one), or null when every caller rings through. Callers not on it are declined. Always null on numbers that can't take calls."
},
"whatsappAvatarUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "URL of the number's WhatsApp avatar; null when unset"
},
"inboundInstruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "System prompt for inbound calls"
},
"inboundVoiceGender": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Voice gender for inbound calls (\"male\"/\"female\"); null → female (the default)"
}
},
"additionalProperties": false
},
"profileResults": {
"type": "object",
"properties": {
"imessage": {
"type": "object",
"required": [
"status"
],
"properties": {
"code": {
"type": "string"
},
"error": {
"type": "string",
"description": "On a failure: plain-language reason the channel's profile wasn't updated"
},
"status": {
"enum": [
"updated",
"failed"
],
"type": "string",
"description": "What happened to this channel's profile"
},
"httpStatus": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "On a failure: the HTTP status it maps to (400 rejected value or channel not on the number; 5xx failure applying it)"
}
},
"additionalProperties": false
},
"whatsapp": {
"type": "object",
"required": [
"status"
],
"properties": {
"code": {
"type": "string"
},
"error": {
"type": "string",
"description": "On a failure: plain-language reason the channel's profile wasn't updated"
},
"status": {
"enum": [
"updated",
"failed"
],
"type": "string",
"description": "What happened to this channel's profile"
},
"httpStatus": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "On a failure: the HTTP status it maps to (400 rejected value or channel not on the number; 5xx failure applying it)"
}
},
"additionalProperties": false
}
},
"description": "Present whenever the update targeted a display profile: each targeted channel's outcome. A `failed` channel was left unchanged while the other channel and the rest of the update were saved. Fix the cause and retry just that channel.",
"additionalProperties": false
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description95/100
Send a message from one of your Dial numbers — to a phone number, or into a group conversation — optionally with media attachments (MMS). On an iMessage number, a single audio attachment is delivered as a voice message unless forceAudioFile is true. Address it with exactly one of to or groupId. A WhatsApp send — one-to-one or into a group — carries at most one attachment, and a body may accompany it only for an image or video; an iMessage group takes media like any other iMessage conversation. A poll (poll: { title, options }) is iMessage-only. Returns the sent message with its id and initial status; delivery updates follow as message events and in list_messages.
Describe the returned information so the agent knows whether it will answer the request.
Inputs89/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
to
Phone number to message, in E.164 form such as +14155550123. Provide exactly one of to or groupId
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
groupId
Id of a group conversation to send into, from list_groups, instead of a to number. The sending line comes from the group, so no from-number is needed, and neither is channel — the group already knows which one it is on, and naming a different one is refused. Provide exactly one of to or groupId
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
channel
Which channel to send on, for a line that carries more than one. Omit to use the number's own default: a number without iMessage sends SMS, an iMessage number sends iMessage, and a number with SMS and WhatsApp sends SMS, so WhatsApp must be asked for by name. WhatsApp works only once the line's whatsapp.status is "ready" (see list_numbers), and iMessage only on an iMessage number
Clear enough to supply this argument.
body
The message text. It can be left out when mediaUrls carries the content (a media-only send)
Clear enough to supply this argument.
fromNumber
Which of your numbers sends it: its id, its E.164 number or its nickname, as list_numbers shows them. Use instead of fromNumberId, not with it; omit both to use your primary number
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
fromNumberId
Id of the number to send from (the id field from list_numbers). Exclusive with fromNumber; omit both to use your primary number
Clear enough to supply this argument.
mediaUrls
Public http(s) links to images, video, audio or files to attach. Dial downloads and re-hosts each one, so they only need to be reachable at send time
Clear enough to supply this argument.
forceAudioFile
Send a lone audio attachment as a regular file instead of a voice message — an iMessage voice message on iMessage numbers, a WhatsApp voice note on WhatsApp numbers. No effect on standard numbers or non-audio media.
Clear enough to supply this argument.
poll › title
The poll question; it becomes the message body
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
poll › options
The answer choices, in the order recipients see them, each one different
Align the output schema with the tool’s documented result.
View output schema
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"message"
],
"properties": {
"message": {
"type": "object",
"required": [
"id",
"from",
"to",
"body",
"status"
],
"properties": {
"id": {
"type": "string"
},
"to": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Recipient, E.164 — null on a group message, whose destination is groupId"
},
"body": {
"type": "string"
},
"from": {
"type": "string",
"description": "Sender, E.164. On a group message, the participant who sent it"
},
"poll": {
"anyOf": [
{
"type": "object",
"required": [
"options"
],
"properties": {
"options": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"text",
"votes"
],
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"votes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Set on a poll message: its options with Dial-issued ids and the current voters per option; null otherwise"
},
"media": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"url",
"contentType",
"originalUrl"
],
"properties": {
"id": {
"type": "string",
"description": "Public media id"
},
"url": {
"type": "string",
"description": "Stable unauthenticated URL serving the media"
},
"contentType": {
"type": "string"
},
"originalUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Caller-supplied source URL on outbound sends; null on inbound messages and direct uploads"
}
},
"additionalProperties": false
},
"description": "Media attachments (MMS / iMessage); empty for text-only messages"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable status, e.g. \"Completed\""
},
"state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Lifecycle state, e.g. \"Terminated\", \"Registered\""
},
"cancelPending": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cancelRequested": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"terminationType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "How it ended, e.g. \"completed\", \"no-answer\", or \"blocked\" when an allow list declined it (null until the call ends)"
}
},
"additionalProperties": {}
}
],
"description": "Status — a plain string, or a structured call-status object"
},
"channel": {
"type": "string"
},
"groupId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The group this message belongs to, or null for a one-to-one conversation"
},
"pollVote": {
"anyOf": [
{
"type": "object",
"required": [
"optionId",
"selected"
],
"properties": {
"optionId": {
"type": "string"
},
"selected": {
"type": "boolean"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Set on an inbound vote on one of your polls (replyToId is the poll); selected false is a retracted vote"
},
"reaction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The reaction this message carries (a reaction name or an emoji); null otherwise"
},
"createdAt": {
"type": "string"
},
"direction": {
"type": "string",
"description": "inbound or outbound"
},
"replyToId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Id of the message this one replies or reacts to; null for ordinary messages"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "send_message",
"title": "Send message",
"description": "Send a message from one of your Dial numbers — to a phone number, or into a group conversation — optionally with media attachments (MMS). On an iMessage number, a single audio attachment is delivered as a voice message unless forceAudioFile is true. Address it with exactly one of to or groupId. A WhatsApp send — one-to-one or into a group — carries at most one attachment, and a body may accompany it only for an image or video; an iMessage group takes media like any other iMessage conversation. A poll (poll: { title, options }) is iMessage-only. Returns the sent message with its id and initial status; delivery updates follow as message events and in list_messages.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"to": {
"type": "string",
"minLength": 7,
"description": "Phone number to message, in E.164 form such as +14155550123. Provide exactly one of to or groupId"
},
"body": {
"type": "string",
"description": "The message text. It can be left out when mediaUrls carries the content (a media-only send)"
},
"poll": {
"type": "object",
"required": [
"title",
"options"
],
"properties": {
"title": {
"type": "string",
"maxLength": 300,
"minLength": 1,
"description": "The poll question; it becomes the message body"
},
"options": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 10,
"minItems": 2,
"description": "The answer choices, in the order recipients see them, each one different"
}
},
"description": "Send a native poll instead of text (iMessage numbers only, one-to-one or into an iMessage group). Exclusive with body, mediaUrls and forceAudioFile. Votes arrive as message.received events with pollVote; the poll message's poll.options[].votes is the live tally (see list_messages)."
},
"channel": {
"enum": [
"sms",
"imessage",
"whatsapp"
],
"type": "string",
"description": "Which channel to send on, for a line that carries more than one. Omit to use the number's own default: a number without iMessage sends SMS, an iMessage number sends iMessage, and a number with SMS and WhatsApp sends SMS, so WhatsApp must be asked for by name. WhatsApp works only once the line's whatsapp.status is \"ready\" (see list_numbers), and iMessage only on an iMessage number"
},
"groupId": {
"type": "string",
"minLength": 1,
"description": "Id of a group conversation to send into, from list_groups, instead of a to number. The sending line comes from the group, so no from-number is needed, and neither is channel — the group already knows which one it is on, and naming a different one is refused. Provide exactly one of to or groupId"
},
"mediaUrls": {
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"maxItems": 10,
"description": "Public http(s) links to images, video, audio or files to attach. Dial downloads and re-hosts each one, so they only need to be reachable at send time"
},
"fromNumber": {
"type": "string",
"minLength": 1,
"description": "Which of your numbers sends it: its id, its E.164 number or its nickname, as list_numbers shows them. Use instead of fromNumberId, not with it; omit both to use your primary number"
},
"fromNumberId": {
"type": "string",
"description": "Id of the number to send from (the id field from list_numbers). Exclusive with fromNumber; omit both to use your primary number"
},
"forceAudioFile": {
"type": "boolean",
"description": "Send a lone audio attachment as a regular file instead of a voice message — an iMessage voice message on iMessage numbers, a WhatsApp voice note on WhatsApp numbers. No effect on standard numbers or non-audio media."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"message"
],
"properties": {
"message": {
"type": "object",
"required": [
"id",
"from",
"to",
"body",
"status"
],
"properties": {
"id": {
"type": "string"
},
"to": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Recipient, E.164 — null on a group message, whose destination is groupId"
},
"body": {
"type": "string"
},
"from": {
"type": "string",
"description": "Sender, E.164. On a group message, the participant who sent it"
},
"poll": {
"anyOf": [
{
"type": "object",
"required": [
"options"
],
"properties": {
"options": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"text",
"votes"
],
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"votes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Set on a poll message: its options with Dial-issued ids and the current voters per option; null otherwise"
},
"media": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"url",
"contentType",
"originalUrl"
],
"properties": {
"id": {
"type": "string",
"description": "Public media id"
},
"url": {
"type": "string",
"description": "Stable unauthenticated URL serving the media"
},
"contentType": {
"type": "string"
},
"originalUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Caller-supplied source URL on outbound sends; null on inbound messages and direct uploads"
}
},
"additionalProperties": false
},
"description": "Media attachments (MMS / iMessage); empty for text-only messages"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable status, e.g. \"Completed\""
},
"state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Lifecycle state, e.g. \"Terminated\", \"Registered\""
},
"cancelPending": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cancelRequested": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"terminationType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "How it ended, e.g. \"completed\", \"no-answer\", or \"blocked\" when an allow list declined it (null until the call ends)"
}
},
"additionalProperties": {}
}
],
"description": "Status — a plain string, or a structured call-status object"
},
"channel": {
"type": "string"
},
"groupId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The group this message belongs to, or null for a one-to-one conversation"
},
"pollVote": {
"anyOf": [
{
"type": "object",
"required": [
"optionId",
"selected"
],
"properties": {
"optionId": {
"type": "string"
},
"selected": {
"type": "boolean"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Set on an inbound vote on one of your polls (replyToId is the poll); selected false is a retracted vote"
},
"reaction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The reaction this message carries (a reaction name or an emoji); null otherwise"
},
"createdAt": {
"type": "string"
},
"direction": {
"type": "string",
"description": "inbound or outbound"
},
"replyToId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Id of the message this one replies or reacts to; null for ordinary messages"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Reply in-thread or react to an existing message. The reply goes out from the Dial number the target message belongs to, to the other party — no from/to needed. Provide exactly one of body or reaction. On iMessage numbers replies thread and reactions are native; recipients that can only receive SMS get an emoji reaction as a regular text, and named reactions are rejected. On an iMessage number, a body reply needs a target that arrived over iMessage or RCS — one that came in over SMS carries no thread to attach to and is rejected, so send a new message with send_message instead of retrying. On WhatsApp numbers both are native, in a one-to-one conversation or a group: a reaction lands on the target message, and a body is a quoted reply. Returns the reply or reaction as a new message, with its id and initial status.
The description explains the capability, when to use it, and what to expect.
Inputs95/100
Input documentation gives an agent enough context to populate the arguments.
messageId
Id of the message to reply or react to (from list_messages or a message.received event)
Clear enough to supply this argument.
body
Reply text; on an iMessage number it threads under the target message, which must itself have arrived over iMessage or RCS
Clear enough to supply this argument.
reaction
Reaction to send instead of a body: love, like, dislike, laugh, emphasize, question, or a single emoji
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"message"
],
"properties": {
"message": {
"type": "object",
"required": [
"id",
"from",
"to",
"body",
"status"
],
"properties": {
"id": {
"type": "string"
},
"to": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Recipient, E.164 — null on a group message, whose destination is groupId"
},
"body": {
"type": "string"
},
"from": {
"type": "string",
"description": "Sender, E.164. On a group message, the participant who sent it"
},
"poll": {
"anyOf": [
{
"type": "object",
"required": [
"options"
],
"properties": {
"options": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"text",
"votes"
],
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"votes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Set on a poll message: its options with Dial-issued ids and the current voters per option; null otherwise"
},
"media": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"url",
"contentType",
"originalUrl"
],
"properties": {
"id": {
"type": "string",
"description": "Public media id"
},
"url": {
"type": "string",
"description": "Stable unauthenticated URL serving the media"
},
"contentType": {
"type": "string"
},
"originalUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Caller-supplied source URL on outbound sends; null on inbound messages and direct uploads"
}
},
"additionalProperties": false
},
"description": "Media attachments (MMS / iMessage); empty for text-only messages"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable status, e.g. \"Completed\""
},
"state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Lifecycle state, e.g. \"Terminated\", \"Registered\""
},
"cancelPending": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cancelRequested": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"terminationType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "How it ended, e.g. \"completed\", \"no-answer\", or \"blocked\" when an allow list declined it (null until the call ends)"
}
},
"additionalProperties": {}
}
],
"description": "Status — a plain string, or a structured call-status object"
},
"channel": {
"type": "string"
},
"groupId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The group this message belongs to, or null for a one-to-one conversation"
},
"pollVote": {
"anyOf": [
{
"type": "object",
"required": [
"optionId",
"selected"
],
"properties": {
"optionId": {
"type": "string"
},
"selected": {
"type": "boolean"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Set on an inbound vote on one of your polls (replyToId is the poll); selected false is a retracted vote"
},
"reaction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The reaction this message carries (a reaction name or an emoji); null otherwise"
},
"createdAt": {
"type": "string"
},
"direction": {
"type": "string",
"description": "inbound or outbound"
},
"replyToId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Id of the message this one replies or reacts to; null for ordinary messages"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "reply_to_message",
"title": "Reply to a message",
"description": "Reply in-thread or react to an existing message. The reply goes out from the Dial number the target message belongs to, to the other party — no from/to needed. Provide exactly one of body or reaction. On iMessage numbers replies thread and reactions are native; recipients that can only receive SMS get an emoji reaction as a regular text, and named reactions are rejected. On an iMessage number, a body reply needs a target that arrived over iMessage or RCS — one that came in over SMS carries no thread to attach to and is rejected, so send a new message with send_message instead of retrying. On WhatsApp numbers both are native, in a one-to-one conversation or a group: a reaction lands on the target message, and a body is a quoted reply. Returns the reply or reaction as a new message, with its id and initial status.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"messageId"
],
"properties": {
"body": {
"type": "string",
"description": "Reply text; on an iMessage number it threads under the target message, which must itself have arrived over iMessage or RCS"
},
"reaction": {
"type": "string",
"description": "Reaction to send instead of a body: love, like, dislike, laugh, emphasize, question, or a single emoji"
},
"messageId": {
"type": "string",
"description": "Id of the message to reply or react to (from list_messages or a message.received event)"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"message"
],
"properties": {
"message": {
"type": "object",
"required": [
"id",
"from",
"to",
"body",
"status"
],
"properties": {
"id": {
"type": "string"
},
"to": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Recipient, E.164 — null on a group message, whose destination is groupId"
},
"body": {
"type": "string"
},
"from": {
"type": "string",
"description": "Sender, E.164. On a group message, the participant who sent it"
},
"poll": {
"anyOf": [
{
"type": "object",
"required": [
"options"
],
"properties": {
"options": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"text",
"votes"
],
"properties": {
"id": {
"type": "string"
},
"text": {
"type": "string"
},
"votes": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Set on a poll message: its options with Dial-issued ids and the current voters per option; null otherwise"
},
"media": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"url",
"contentType",
"originalUrl"
],
"properties": {
"id": {
"type": "string",
"description": "Public media id"
},
"url": {
"type": "string",
"description": "Stable unauthenticated URL serving the media"
},
"contentType": {
"type": "string"
},
"originalUrl": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Caller-supplied source URL on outbound sends; null on inbound messages and direct uploads"
}
},
"additionalProperties": false
},
"description": "Media attachments (MMS / iMessage); empty for text-only messages"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable status, e.g. \"Completed\""
},
"state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Lifecycle state, e.g. \"Terminated\", \"Registered\""
},
"cancelPending": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cancelRequested": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"terminationType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "How it ended, e.g. \"completed\", \"no-answer\", or \"blocked\" when an allow list declined it (null until the call ends)"
}
},
"additionalProperties": {}
}
],
"description": "Status — a plain string, or a structured call-status object"
},
"channel": {
"type": "string"
},
"groupId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The group this message belongs to, or null for a one-to-one conversation"
},
"pollVote": {
"anyOf": [
{
"type": "object",
"required": [
"optionId",
"selected"
],
"properties": {
"optionId": {
"type": "string"
},
"selected": {
"type": "boolean"
}
},
"additionalProperties": false
},
{
"type": "null"
}
],
"description": "Set on an inbound vote on one of your polls (replyToId is the poll); selected false is a retracted vote"
},
"reaction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The reaction this message carries (a reaction name or an emoji); null otherwise"
},
"createdAt": {
"type": "string"
},
"direction": {
"type": "string",
"description": "inbound or outbound"
},
"replyToId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Id of the message this one replies or reacts to; null for ordinary messages"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description99/100
Show a typing indicator to the recipient, as if someone were composing a message from your number. Address a group with groupId instead of toNumber and every member sees it. iMessage numbers display it; standard (SMS) numbers have no typing concept and silently ignore it, so this is safe to call unconditionally before a send. Fire-and-forget and free. Delivering a message or reaction clears the indicator natively on the recipient's device — call start_typing again after a send to keep composing, and stop_typing when you stop without sending. Returns ok true.
The description explains the capability, when to use it, and what to expect.
Inputs81/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
toNumber
Phone number of the person who sees the indicator, in E.164 form such as +14155550123. Provide exactly one of toNumber or groupId
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
groupId
Id of a group conversation, from list_groups, to show the indicator to every member of instead. The group already names the line it is on. Provide exactly one of toNumber or groupId
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
fromNumber
Which of your numbers the indicator appears from: its id, its E.164 number or its nickname, as list_numbers shows them. Needed with toNumber; with groupId the group already names its line
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
channel
Which channel to show it on, for a line that carries more than one. Omit to use the number's own default, and omit it with groupId, since the group already names its channel. SMS has no typing indicator, so on SMS the call succeeds without showing anything
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean",
"description": "true once the request is accepted, including on SMS, where nothing is shown"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "start_typing",
"title": "Start typing indicator",
"description": "Show a typing indicator to the recipient, as if someone were composing a message from your number. Address a group with groupId instead of toNumber and every member sees it. iMessage numbers display it; standard (SMS) numbers have no typing concept and silently ignore it, so this is safe to call unconditionally before a send. Fire-and-forget and free. Delivering a message or reaction clears the indicator natively on the recipient's device — call start_typing again after a send to keep composing, and stop_typing when you stop without sending. Returns ok true.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"channel": {
"enum": [
"sms",
"imessage",
"whatsapp"
],
"type": "string",
"description": "Which channel to show it on, for a line that carries more than one. Omit to use the number's own default, and omit it with groupId, since the group already names its channel. SMS has no typing indicator, so on SMS the call succeeds without showing anything"
},
"groupId": {
"type": "string",
"minLength": 1,
"description": "Id of a group conversation, from list_groups, to show the indicator to every member of instead. The group already names the line it is on. Provide exactly one of toNumber or groupId"
},
"toNumber": {
"type": "string",
"minLength": 7,
"description": "Phone number of the person who sees the indicator, in E.164 form such as +14155550123. Provide exactly one of toNumber or groupId"
},
"fromNumber": {
"type": "string",
"minLength": 1,
"description": "Which of your numbers the indicator appears from: its id, its E.164 number or its nickname, as list_numbers shows them. Needed with toNumber; with groupId the group already names its line"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean",
"description": "true once the request is accepted, including on SMS, where nothing is shown"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description99/100
Clear a typing indicator previously shown with start_typing. Delivering a message or reaction already clears it natively on the recipient's device — call this when you stop composing without sending. Standard (SMS) numbers silently ignore it. Returns ok true.
The description explains the capability, when to use it, and what to expect.
Inputs94/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
toNumber
Phone number of the person who sees the indicator, in E.164 form such as +14155550123. Provide exactly one of toNumber or groupId
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
groupId
Id of a group conversation, from list_groups, to show the indicator to every member of instead. The group already names the line it is on. Provide exactly one of toNumber or groupId
Clear enough to supply this argument.
fromNumber
Which of your numbers the indicator appears from: its id, its E.164 number or its nickname, as list_numbers shows them. Needed with toNumber; with groupId the group already names its line
Clear enough to supply this argument.
channel
Which channel to clear it on, for a line that carries more than one. Omit to use the number's own default, and omit it with groupId, since the group already names its channel. SMS has no typing indicator, so on SMS the call succeeds without doing anything
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean",
"description": "true once the request is accepted, including on SMS, where nothing is shown"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "stop_typing",
"title": "Stop typing indicator",
"description": "Clear a typing indicator previously shown with start_typing. Delivering a message or reaction already clears it natively on the recipient's device — call this when you stop composing without sending. Standard (SMS) numbers silently ignore it. Returns ok true.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"channel": {
"enum": [
"sms",
"imessage",
"whatsapp"
],
"type": "string",
"description": "Which channel to clear it on, for a line that carries more than one. Omit to use the number's own default, and omit it with groupId, since the group already names its channel. SMS has no typing indicator, so on SMS the call succeeds without doing anything"
},
"groupId": {
"type": "string",
"minLength": 1,
"description": "Id of a group conversation, from list_groups, to show the indicator to every member of instead. The group already names the line it is on. Provide exactly one of toNumber or groupId"
},
"toNumber": {
"type": "string",
"minLength": 7,
"description": "Phone number of the person who sees the indicator, in E.164 form such as +14155550123. Provide exactly one of toNumber or groupId"
},
"fromNumber": {
"type": "string",
"minLength": 1,
"description": "Which of your numbers the indicator appears from: its id, its E.164 number or its nickname, as list_numbers shows them. Needed with toNumber; with groupId the group already names its line"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"ok"
],
"properties": {
"ok": {
"type": "boolean",
"description": "true once the request is accepted, including on SMS, where nothing is shown"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description80/100
List recent messages on your account, newest first (up to 100). Pass `contact` to read one person's whole conversation, or `groupId` to read one group's. Pass `search` to find messages by their text — that searches all of your history, then returns the 100 most recent matches. On a group message `to` is null and the destination is groupId; which of your numbers the conversation is on is phoneNumberId.
The description explains the capability, when to use it, and what to expect.
Inputs96/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
search
Text to find inside message bodies, matched case-insensitively anywhere in the text; searches your whole history, not only the latest messages
Clear enough to supply this argument.
contact
One conversation: messages exchanged with this number (E.164), both directions, across every line. Group messages are never included — use groupId for those
Clear enough to supply this argument.
numberId
Only messages on this one of your numbers: its id from list_numbers
Clear enough to supply this argument.
groupId
Only messages in this group conversation: its id from list_groups. Combines with the other filters
Clear enough to supply this argument.
direction
inbound for messages you received, outbound for messages you sent; omit for both
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
since
Only messages sent or received after this moment, as an ISO-8601 timestamp such as 2026-10-01T00:00:00Z
The title clearly communicates the tool’s action and subject. Keep it concise.
Description89/100
List the group conversations your lines are in. A group is a conversation that isn't a phone number, so it has an id of its own: pass it as groupId to send_message, or to list_messages to read that conversation. Groups exist on WhatsApp lines and iMessage numbers. A group's name can be null — it is read live from the line holding the conversation, so a line that can't answer in time yields a null name rather than hiding the group. There is no join event: list again to see a group your line was just added to.
The description explains the capability, when to use it, and what to expect.
Inputs100/100
This tool declares no inputs, so argument descriptions are not needed.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"groups"
],
"properties": {
"groups": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"channel",
"name"
],
"properties": {
"id": {
"type": "string",
"description": "Group id — pass as groupId to send_message or list_messages"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The group's current name, or null when no line could report it in time"
},
"channel": {
"enum": [
"whatsapp",
"imessage"
],
"type": "string",
"description": "The channel the group is on; its rules apply to anything sent into the group"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when Dial first learned of this group (a join, or its first message)"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "list_groups",
"title": "List Groups",
"description": "List the group conversations your lines are in. A group is a conversation that isn't a phone number, so it has an id of its own: pass it as groupId to send_message, or to list_messages to read that conversation. Groups exist on WhatsApp lines and iMessage numbers. A group's name can be null — it is read live from the line holding the conversation, so a line that can't answer in time yields a null name rather than hiding the group. There is no join event: list again to see a group your line was just added to.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"groups"
],
"properties": {
"groups": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"channel",
"name"
],
"properties": {
"id": {
"type": "string",
"description": "Group id — pass as groupId to send_message or list_messages"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The group's current name, or null when no line could report it in time"
},
"channel": {
"enum": [
"whatsapp",
"imessage"
],
"type": "string",
"description": "The channel the group is on; its rules apply to anything sent into the group"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when Dial first learned of this group (a join, or its first message)"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Create a WhatsApp group from one of your lines, which becomes its admin. The new group's id works at once: pass it as groupId to send_message or list_messages, and it appears in list_groups. Some participants can end up outside the group even though it was created — notAdded lists each one with a reason (privacy_restricted means their privacy settings only let contacts add them). A timeout means the group may still exist: check list_groups before trying again, or you may create it twice. Returns `group` (its id and name) and `notAdded`.
The description explains the capability, when to use it, and what to expect.
Inputs88/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
fromNumber
Which of your numbers creates the group and becomes its admin: its id, its E.164 number or its nickname, as list_numbers shows them. Use instead of fromNumberId, not with it; omit both to use your primary number. Its whatsapp.status must be "ready"
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
fromNumberId
Id of the number to create the group from (the id field from list_numbers). Exclusive with fromNumber; omit both to use your primary number
Clear enough to supply this argument.
name
The group's name, shown to every participant
Clear enough to supply this argument.
participants
Phone numbers of the people to add, in E.164 form such as +14155550123. Duplicates are dropped, and the creating line's own number is refused
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"group",
"notAdded"
],
"properties": {
"group": {
"type": "object",
"required": [
"id",
"channel",
"name"
],
"properties": {
"id": {
"type": "string",
"description": "Group id — pass as groupId to send_message or list_messages"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The group's current name, or null when no line could report it in time"
},
"channel": {
"enum": [
"whatsapp",
"imessage"
],
"type": "string",
"description": "The channel the group is on; its rules apply to anything sent into the group"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when Dial first learned of this group (a join, or its first message)"
}
},
"additionalProperties": false
},
"notAdded": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"reason"
],
"properties": {
"number": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The participant, E.164 — null only when the line could not name them"
},
"reason": {
"type": "string",
"description": "privacy_restricted: their privacy settings only let contacts add them to groups. not_added: anything else"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "create_group",
"title": "Create Group",
"description": "Create a WhatsApp group from one of your lines, which becomes its admin. The new group's id works at once: pass it as groupId to send_message or list_messages, and it appears in list_groups. Some participants can end up outside the group even though it was created — notAdded lists each one with a reason (privacy_restricted means their privacy settings only let contacts add them). A timeout means the group may still exist: check list_groups before trying again, or you may create it twice. Returns `group` (its id and name) and `notAdded`.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"name",
"participants"
],
"properties": {
"name": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "The group's name, shown to every participant"
},
"fromNumber": {
"type": "string",
"minLength": 1,
"description": "Which of your numbers creates the group and becomes its admin: its id, its E.164 number or its nickname, as list_numbers shows them. Use instead of fromNumberId, not with it; omit both to use your primary number. Its whatsapp.status must be \"ready\""
},
"fromNumberId": {
"type": "string",
"description": "Id of the number to create the group from (the id field from list_numbers). Exclusive with fromNumber; omit both to use your primary number"
},
"participants": {
"type": "array",
"items": {
"type": "string",
"minLength": 7
},
"maxItems": 256,
"minItems": 1,
"description": "Phone numbers of the people to add, in E.164 form such as +14155550123. Duplicates are dropped, and the creating line's own number is refused"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"group",
"notAdded"
],
"properties": {
"group": {
"type": "object",
"required": [
"id",
"channel",
"name"
],
"properties": {
"id": {
"type": "string",
"description": "Group id — pass as groupId to send_message or list_messages"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The group's current name, or null when no line could report it in time"
},
"channel": {
"enum": [
"whatsapp",
"imessage"
],
"type": "string",
"description": "The channel the group is on; its rules apply to anything sent into the group"
},
"createdAt": {
"type": "string",
"description": "ISO-8601 timestamp: when Dial first learned of this group (a join, or its first message)"
}
},
"additionalProperties": false
},
"notAdded": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"reason"
],
"properties": {
"number": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The participant, E.164 — null only when the line could not name them"
},
"reason": {
"type": "string",
"description": "privacy_restricted: their privacy settings only let contacts add them to groups. not_added: anything else"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description96/100
List every number your lines have exchanged a message or a call with, newest activity first, with per-contact counts and a summary of the most recent interaction. Derived from history — there is no address book to add anyone to. Counts span every line on the account, so one person reached on two of your numbers is one contact — pass `numberId` to see one line's contacts and counts instead. Group conversations are not contacts; use list_groups for those. Pass a contact's `number` to list_messages as `contact` to read that one conversation.
The description explains the capability, when to use it, and what to expect.
Inputs92/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
numberId
Only the contacts of one of your numbers, by its id from list_numbers; counts and the latest interaction are then that line's alone
Clear enough to supply this argument.
limit
How many contacts to return in this page; 100 when omitted. hasMore says whether older ones remain
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
startingAfter
Pagination cursor: the lastAt value of the last contact on the previous page. Returns only contacts whose latest activity is older
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"contacts",
"hasMore"
],
"properties": {
"hasMore": {
"type": "boolean"
},
"contacts": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"messageCount",
"callCount",
"lastAt",
"lastDirection",
"lastKind",
"lastBody",
"lastMediaCount",
"lastRedacted",
"lastCallDuration"
],
"properties": {
"lastAt": {
"type": "string",
"description": "ISO-8601 timestamp of the most recent message or call"
},
"number": {
"type": "string",
"description": "The contact's number, E.164 — pass as `contact` to list_messages"
},
"lastBody": {
"type": "string",
"description": "The most recent message's text. Empty for a call, a media-only message, or a redacted one"
},
"lastKind": {
"enum": [
"message",
"call"
],
"type": "string",
"description": "What the most recent interaction was"
},
"callCount": {
"type": "number",
"description": "Calls with this contact across every line on the account, both directions"
},
"lastRedacted": {
"type": "boolean",
"description": "True when data retention cleared the most recent message's content"
},
"messageCount": {
"type": "number",
"description": "One-to-one messages with this contact across every line on the account, both directions"
},
"lastDirection": {
"enum": [
"inbound",
"outbound"
],
"type": "string",
"description": "Whether the most recent interaction came from them or you"
},
"lastMediaCount": {
"type": "number",
"description": "Attachments on the most recent message; 0 for a call or a text-only message"
},
"lastCallDuration": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "The most recent call's duration in seconds, or null when the most recent interaction was a message"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "list_contacts",
"title": "List Contacts",
"description": "List every number your lines have exchanged a message or a call with, newest activity first, with per-contact counts and a summary of the most recent interaction. Derived from history — there is no address book to add anyone to. Counts span every line on the account, so one person reached on two of your numbers is one contact — pass `numberId` to see one line's contacts and counts instead. Group conversations are not contacts; use list_groups for those. Pass a contact's `number` to list_messages as `contact` to read that one conversation.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"limit": {
"type": "integer",
"maximum": 1000,
"minimum": 1,
"description": "How many contacts to return in this page; 100 when omitted. hasMore says whether older ones remain"
},
"numberId": {
"type": "string",
"description": "Only the contacts of one of your numbers, by its id from list_numbers; counts and the latest interaction are then that line's alone"
},
"startingAfter": {
"type": "string",
"description": "Pagination cursor: the lastAt value of the last contact on the previous page. Returns only contacts whose latest activity is older"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"contacts",
"hasMore"
],
"properties": {
"hasMore": {
"type": "boolean"
},
"contacts": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"messageCount",
"callCount",
"lastAt",
"lastDirection",
"lastKind",
"lastBody",
"lastMediaCount",
"lastRedacted",
"lastCallDuration"
],
"properties": {
"lastAt": {
"type": "string",
"description": "ISO-8601 timestamp of the most recent message or call"
},
"number": {
"type": "string",
"description": "The contact's number, E.164 — pass as `contact` to list_messages"
},
"lastBody": {
"type": "string",
"description": "The most recent message's text. Empty for a call, a media-only message, or a redacted one"
},
"lastKind": {
"enum": [
"message",
"call"
],
"type": "string",
"description": "What the most recent interaction was"
},
"callCount": {
"type": "number",
"description": "Calls with this contact across every line on the account, both directions"
},
"lastRedacted": {
"type": "boolean",
"description": "True when data retention cleared the most recent message's content"
},
"messageCount": {
"type": "number",
"description": "One-to-one messages with this contact across every line on the account, both directions"
},
"lastDirection": {
"enum": [
"inbound",
"outbound"
],
"type": "string",
"description": "Whether the most recent interaction came from them or you"
},
"lastMediaCount": {
"type": "number",
"description": "Attachments on the most recent message; 0 for a call or a text-only message"
},
"lastCallDuration": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "The most recent call's duration in seconds, or null when the most recent interaction was a message"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description99/100
Check what channels a phone number can receive on, before sending to it. Works on ANY number in the world — it doesn't have to be one of your numbers, and you don't have to have messaged it before. `supports` describes the number you asked about, not your own line: each key is a channel and the boolean says whether that number can be reached there. Read the channels you need by name, since more are added over time. The answer is point-in-time, not a property of the number — someone who changes device or turns the service off stops being reachable — so treat `true` as a strong signal for picking a channel rather than a promise that the send will land. A lookup that fails is an error, never a `false`, so a `false` always means the number genuinely isn't reachable there. It needs nothing from this account: you don't have to hold an iMessage or WhatsApp number to ask about either channel.
The description explains the capability, when to use it, and what to expect.
Inputs99/100
Input documentation gives an agent enough context to populate the arguments.
number
The phone number to look up, in E.164 — e.g. +14155550123. Any number, not just your own.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"number",
"supports"
],
"properties": {
"number": {
"type": "string",
"description": "The number you asked about, normalized to E.164"
},
"supports": {
"type": "object",
"required": [
"imessage",
"whatsapp"
],
"properties": {
"imessage": {
"type": "boolean",
"description": "Whether the number can currently receive iMessage"
},
"whatsapp": {
"type": "boolean",
"description": "Whether the number can currently receive WhatsApp"
}
},
"description": "One key per channel, true when the number can be reached there",
"additionalProperties": false
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "lookup_number",
"title": "Look Up A Number",
"description": "Check what channels a phone number can receive on, before sending to it. Works on ANY number in the world — it doesn't have to be one of your numbers, and you don't have to have messaged it before. `supports` describes the number you asked about, not your own line: each key is a channel and the boolean says whether that number can be reached there. Read the channels you need by name, since more are added over time. The answer is point-in-time, not a property of the number — someone who changes device or turns the service off stops being reachable — so treat `true` as a strong signal for picking a channel rather than a promise that the send will land. A lookup that fails is an error, never a `false`, so a `false` always means the number genuinely isn't reachable there. It needs nothing from this account: you don't have to hold an iMessage or WhatsApp number to ask about either channel.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"number"
],
"properties": {
"number": {
"type": "string",
"description": "The phone number to look up, in E.164 — e.g. +14155550123. Any number, not just your own."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"number",
"supports"
],
"properties": {
"number": {
"type": "string",
"description": "The number you asked about, normalized to E.164"
},
"supports": {
"type": "object",
"required": [
"imessage",
"whatsapp"
],
"properties": {
"imessage": {
"type": "boolean",
"description": "Whether the number can currently receive iMessage"
},
"whatsapp": {
"type": "boolean",
"description": "Whether the number can currently receive WhatsApp"
}
},
"description": "One key per channel, true when the number can be reached there",
"additionalProperties": false
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Place an outbound voice call handled by an AI agent. The call runs asynchronously — use wait_for_event to block until it ends, then get_call for the transcript. Free accounts (no top-up or subscription yet) are capped at 5 minutes per call and 2 concurrent calls (429 call_limit_reached); asking for a longer call explicitly is rejected with 400 rather than shortened. Both limits lift on the first top-up or subscription. Returns the call (its id and initial status) and a hint naming the exact wait_for_event and get_call arguments to use next.
The description explains the capability, when to use it, and what to expect.
Inputs89/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
to
Phone number to call, in E.164 form such as +14155550123
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
outboundInstruction
What the AI voice agent should accomplish on this call and how to behave: its goal, the facts it needs, and its tone. This is the agent's system prompt
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
language
Language the agent speaks, as a BCP-47 tag such as es-ES. Omit to pick it from the destination number's country (with en-US alongside).
Clear enough to supply this argument.
voiceGender
Whether the agent speaks with a male or a female voice; female when omitted
Clear enough to supply this argument.
transferTo
A phone number to hand the call to, in E.164 form: the agent waits until a real person picks up (riding out hold music and phone menus), then transfers the call there and leaves. Must differ from `to` and from the number calling
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
maxCallDurationSeconds
Hard cap on the call's length, in seconds; the call is hung up when it is reached. On a free account (no top-up or subscription yet) a value above 300 is rejected with 400, not shortened
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
idempotencyKey
A unique string you choose for this intended call, e.g. a UUID: retrying with the same key returns the call already placed instead of dialing again
Clear enough to supply this argument.
fromNumber
Which of your numbers places the call: its id, its E.164 number or its nickname, as list_numbers shows them. Use instead of fromNumberId, not with it; omit both to use your primary number
Clear enough to supply this argument.
fromNumberId
Id of the number to call from (the id field from list_numbers). Exclusive with fromNumber; omit both to use your primary number
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"call",
"hint"
],
"properties": {
"call": {
"type": "object",
"required": [
"id",
"from",
"to",
"status"
],
"properties": {
"id": {
"type": "string"
},
"to": {
"type": "string",
"description": "Callee, E.164"
},
"from": {
"type": "string",
"description": "Caller, E.164"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable status, e.g. \"Completed\""
},
"state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Lifecycle state, e.g. \"Terminated\", \"Registered\""
},
"cancelPending": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cancelRequested": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"terminationType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "How it ended, e.g. \"completed\", \"no-answer\", or \"blocked\" when an allow list declined it (null until the call ends)"
}
},
"additionalProperties": {}
}
],
"description": "Status — a plain string, or a structured call-status object"
},
"duration": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Call duration in seconds"
},
"createdAt": {
"type": "string"
},
"direction": {
"type": "string"
},
"transcript": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"instruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"failureReason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why the call failed, set only when status.terminationType is \"failed\" and Dial knows: self_hosted_key_rejected, self_hosted_agent_not_found, self_hosted_at_capacity, or self_hosted_unreachable. Null otherwise; treat an unknown value like null."
},
"transcriptTurns": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"required": [
"speaker",
"text",
"startMs",
"endMs"
],
"properties": {
"text": {
"type": "string",
"description": "What was said during the turn"
},
"endMs": {
"type": "number",
"description": "Approximate ms into the call's audio at which the turn ended"
},
"speaker": {
"enum": [
"agent",
"user",
"transfer_target"
],
"type": "string",
"description": "`agent` is Dial's AI voice agent, `user` the human on the other end, and `transfer_target` the human the call was cold-transferred to"
},
"startMs": {
"type": "number",
"description": "Approximate ms into the call's audio at which the turn began"
}
},
"description": "One uninterrupted stretch of speech by one party, placed in time",
"additionalProperties": false
}
},
{
"type": "null"
}
],
"description": "The same conversation as `transcript`, split into timed turns and ordered by startMs. Use it to measure pacing: the pause before a turn is its startMs minus the previous turn's endMs. Null when the call has no transcript, or when the call's turn timing was not recorded."
},
"blockedByPolicyId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The allow list that declined this inbound call when status.terminationType is \"blocked\"; null otherwise"
}
},
"additionalProperties": {}
},
"hint": {
"type": "string",
"description": "Next-step guidance for tracking the call"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "place_call",
"title": "Place AI Voice Call",
"description": "Place an outbound voice call handled by an AI agent. The call runs asynchronously — use wait_for_event to block until it ends, then get_call for the transcript. Free accounts (no top-up or subscription yet) are capped at 5 minutes per call and 2 concurrent calls (429 call_limit_reached); asking for a longer call explicitly is rejected with 400 rather than shortened. Both limits lift on the first top-up or subscription. Returns the call (its id and initial status) and a hint naming the exact wait_for_event and get_call arguments to use next.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"to",
"outboundInstruction"
],
"properties": {
"to": {
"type": "string",
"minLength": 7,
"description": "Phone number to call, in E.164 form such as +14155550123"
},
"language": {
"enum": [
"en-US",
"en-GB",
"en-AU",
"en-IN",
"en-NZ",
"de-DE",
"fr-FR",
"fr-CA",
"es-ES",
"es-419",
"it-IT",
"pt-BR",
"pt-PT",
"nl-NL",
"nl-BE",
"pl-PL",
"ru-RU",
"ro-RO",
"tr-TR",
"he-IL",
"ar-SA",
"hi-IN",
"kn-IN",
"ta-IN",
"ja-JP",
"zh-CN",
"ko-KR",
"vi-VN",
"th-TH",
"ms-MY",
"fil-PH"
],
"type": "string",
"description": "Language the agent speaks, as a BCP-47 tag such as es-ES. Omit to pick it from the destination number's country (with en-US alongside)."
},
"fromNumber": {
"type": "string",
"minLength": 1,
"description": "Which of your numbers places the call: its id, its E.164 number or its nickname, as list_numbers shows them. Use instead of fromNumberId, not with it; omit both to use your primary number"
},
"transferTo": {
"type": "string",
"minLength": 7,
"description": "A phone number to hand the call to, in E.164 form: the agent waits until a real person picks up (riding out hold music and phone menus), then transfers the call there and leaves. Must differ from `to` and from the number calling"
},
"voiceGender": {
"enum": [
"male",
"female"
],
"type": "string",
"description": "Whether the agent speaks with a male or a female voice; female when omitted"
},
"fromNumberId": {
"type": "string",
"description": "Id of the number to call from (the id field from list_numbers). Exclusive with fromNumber; omit both to use your primary number"
},
"idempotencyKey": {
"type": "string",
"maxLength": 255,
"description": "A unique string you choose for this intended call, e.g. a UUID: retrying with the same key returns the call already placed instead of dialing again"
},
"outboundInstruction": {
"type": "string",
"minLength": 1,
"description": "What the AI voice agent should accomplish on this call and how to behave: its goal, the facts it needs, and its tone. This is the agent's system prompt"
},
"maxCallDurationSeconds": {
"type": "integer",
"maximum": 3600,
"minimum": 60,
"description": "Hard cap on the call's length, in seconds; the call is hung up when it is reached. On a free account (no top-up or subscription yet) a value above 300 is rejected with 400, not shortened"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"call",
"hint"
],
"properties": {
"call": {
"type": "object",
"required": [
"id",
"from",
"to",
"status"
],
"properties": {
"id": {
"type": "string"
},
"to": {
"type": "string",
"description": "Callee, E.164"
},
"from": {
"type": "string",
"description": "Caller, E.164"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable status, e.g. \"Completed\""
},
"state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Lifecycle state, e.g. \"Terminated\", \"Registered\""
},
"cancelPending": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cancelRequested": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"terminationType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "How it ended, e.g. \"completed\", \"no-answer\", or \"blocked\" when an allow list declined it (null until the call ends)"
}
},
"additionalProperties": {}
}
],
"description": "Status — a plain string, or a structured call-status object"
},
"duration": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Call duration in seconds"
},
"createdAt": {
"type": "string"
},
"direction": {
"type": "string"
},
"transcript": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"instruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"failureReason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why the call failed, set only when status.terminationType is \"failed\" and Dial knows: self_hosted_key_rejected, self_hosted_agent_not_found, self_hosted_at_capacity, or self_hosted_unreachable. Null otherwise; treat an unknown value like null."
},
"transcriptTurns": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"required": [
"speaker",
"text",
"startMs",
"endMs"
],
"properties": {
"text": {
"type": "string",
"description": "What was said during the turn"
},
"endMs": {
"type": "number",
"description": "Approximate ms into the call's audio at which the turn ended"
},
"speaker": {
"enum": [
"agent",
"user",
"transfer_target"
],
"type": "string",
"description": "`agent` is Dial's AI voice agent, `user` the human on the other end, and `transfer_target` the human the call was cold-transferred to"
},
"startMs": {
"type": "number",
"description": "Approximate ms into the call's audio at which the turn began"
}
},
"description": "One uninterrupted stretch of speech by one party, placed in time",
"additionalProperties": false
}
},
{
"type": "null"
}
],
"description": "The same conversation as `transcript`, split into timed turns and ordered by startMs. Use it to measure pacing: the pause before a turn is its startMs minus the previous turn's endMs. Null when the call has no transcript, or when the call's turn timing was not recorded."
},
"blockedByPolicyId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The allow list that declined this inbound call when status.terminationType is \"blocked\"; null otherwise"
}
},
"additionalProperties": {}
},
"hint": {
"type": "string",
"description": "Next-step guidance for tracking the call"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description92/100
List recent calls on your account, newest first (up to 100), with derived status. Pass `contact` to read one person's call history. Each call carries its id, from, to, direction, status and duration in seconds; pass the id to get_call for the full transcript.
The description explains the capability, when to use it, and what to expect.
Inputs94/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
contact
One conversation: calls exchanged with this number (E.164), both directions, across every line
Clear enough to supply this argument.
numberId
Only calls on this one of your numbers: its id from list_numbers
Clear enough to supply this argument.
direction
inbound for calls to your numbers, outbound for calls you placed; omit for both
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
since
Only calls started after this moment, as an ISO-8601 timestamp such as 2026-10-01T00:00:00Z
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"calls"
],
"properties": {
"calls": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"from",
"to",
"status"
],
"properties": {
"id": {
"type": "string"
},
"to": {
"type": "string",
"description": "Callee, E.164"
},
"from": {
"type": "string",
"description": "Caller, E.164"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable status, e.g. \"Completed\""
},
"state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Lifecycle state, e.g. \"Terminated\", \"Registered\""
},
"cancelPending": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cancelRequested": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"terminationType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "How it ended, e.g. \"completed\", \"no-answer\", or \"blocked\" when an allow list declined it (null until the call ends)"
}
},
"additionalProperties": {}
}
],
"description": "Status — a plain string, or a structured call-status object"
},
"duration": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Call duration in seconds"
},
"createdAt": {
"type": "string"
},
"direction": {
"type": "string"
},
"transcript": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"instruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"failureReason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why the call failed, set only when status.terminationType is \"failed\" and Dial knows: self_hosted_key_rejected, self_hosted_agent_not_found, self_hosted_at_capacity, or self_hosted_unreachable. Null otherwise; treat an unknown value like null."
},
"transcriptTurns": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"required": [
"speaker",
"text",
"startMs",
"endMs"
],
"properties": {
"text": {
"type": "string",
"description": "What was said during the turn"
},
"endMs": {
"type": "number",
"description": "Approximate ms into the call's audio at which the turn ended"
},
"speaker": {
"enum": [
"agent",
"user",
"transfer_target"
],
"type": "string",
"description": "`agent` is Dial's AI voice agent, `user` the human on the other end, and `transfer_target` the human the call was cold-transferred to"
},
"startMs": {
"type": "number",
"description": "Approximate ms into the call's audio at which the turn began"
}
},
"description": "One uninterrupted stretch of speech by one party, placed in time",
"additionalProperties": false
}
},
{
"type": "null"
}
],
"description": "The same conversation as `transcript`, split into timed turns and ordered by startMs. Use it to measure pacing: the pause before a turn is its startMs minus the previous turn's endMs. Null when the call has no transcript, or when the call's turn timing was not recorded."
},
"blockedByPolicyId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The allow list that declined this inbound call when status.terminationType is \"blocked\"; null otherwise"
}
},
"additionalProperties": {}
}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "list_calls",
"title": "List Calls",
"description": "List recent calls on your account, newest first (up to 100), with derived status. Pass `contact` to read one person's call history. Each call carries its id, from, to, direction, status and duration in seconds; pass the id to get_call for the full transcript.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"since": {
"type": "string",
"description": "Only calls started after this moment, as an ISO-8601 timestamp such as 2026-10-01T00:00:00Z"
},
"contact": {
"type": "string",
"description": "One conversation: calls exchanged with this number (E.164), both directions, across every line"
},
"numberId": {
"type": "string",
"description": "Only calls on this one of your numbers: its id from list_numbers"
},
"direction": {
"enum": [
"inbound",
"outbound"
],
"type": "string",
"description": "inbound for calls to your numbers, outbound for calls you placed; omit for both"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"calls"
],
"properties": {
"calls": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"from",
"to",
"status"
],
"properties": {
"id": {
"type": "string"
},
"to": {
"type": "string",
"description": "Callee, E.164"
},
"from": {
"type": "string",
"description": "Caller, E.164"
},
"status": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Human-readable status, e.g. \"Completed\""
},
"state": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Lifecycle state, e.g. \"Terminated\", \"Registered\""
},
"cancelPending": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"cancelRequested": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
]
},
"terminationType": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "How it ended, e.g. \"completed\", \"no-answer\", or \"blocked\" when an allow list declined it (null until the call ends)"
}
},
"additionalProperties": {}
}
],
"description": "Status — a plain string, or a structured call-status object"
},
"duration": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Call duration in seconds"
},
"createdAt": {
"type": "string"
},
"direction": {
"type": "string"
},
"transcript": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"instruction": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"failureReason": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Why the call failed, set only when status.terminationType is \"failed\" and Dial knows: self_hosted_key_rejected, self_hosted_agent_not_found, self_hosted_at_capacity, or self_hosted_unreachable. Null otherwise; treat an unknown value like null."
},
"transcriptTurns": {
"anyOf": [
{
"type": "array",
"items": {
"type": "object",
"required": [
"speaker",
"text",
"startMs",
"endMs"
],
"properties": {
"text": {
"type": "string",
"description": "What was said during the turn"
},
"endMs": {
"type": "number",
"description": "Approximate ms into the call's audio at which the turn ended"
},
"speaker": {
"enum": [
"agent",
"user",
"transfer_target"
],
"type": "string",
"description": "`agent` is Dial's AI voice agent, `user` the human on the other end, and `transfer_target` the human the call was cold-transferred to"
},
"startMs": {
"type": "number",
"description": "Approximate ms into the call's audio at which the turn began"
}
},
"description": "One uninterrupted stretch of speech by one party, placed in time",
"additionalProperties": false
}
},
{
"type": "null"
}
],
"description": "The same conversation as `transcript`, split into timed turns and ordered by startMs. Use it to measure pacing: the pause before a turn is its startMs minus the previous turn's endMs. Null when the call has no transcript, or when the call's turn timing was not recorded."
},
"blockedByPolicyId": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The allow list that declined this inbound call when status.terminationType is \"blocked\"; null otherwise"
}
},
"additionalProperties": {}
}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description93/100
Fetch a single call: status, duration, and transcript. The transcript comes back twice: `transcript` as flat text, and `transcriptTurns` as timed turns for analysing pacing and spotting long pauses.
The description explains the capability, when to use it, and what to expect.
Inputs99/100
Input documentation gives an agent enough context to populate the arguments.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description99/100
End a call that hasn't finished yet. A Queued or Ringing call is cancelled before it connects (it ends as `canceled` and isn't charged); an In-Progress call is hung up (it ends as `completed`). Returns the call with `status.cancelPending: true` — it reaches Terminated a moment later, so use wait_for_event on `call.ended` to confirm. Fails if the call has already ended.
The description explains the capability, when to use it, and what to expect.
Inputs99/100
Input documentation gives an agent enough context to populate the arguments.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Block until a matching account event arrives (e.g. an inbound SMS, or a call ending). Only events that arrive after the call starts count. Returns { event } with the event's type and data, or { status: "timeout", waitedSeconds } when none arrived in time: call it again to keep waiting.
The description explains the capability, when to use it, and what to expect.
Inputs88/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
eventType
Which event to wait for, e.g. message.received for an inbound message or call.ended for a finished call
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
field
Only match events whose field equals a value, each written "name=value", e.g. "callId=..." from place_call. Every filter must match
Clear enough to supply this argument.
regex
Only match events whose field matches a regular expression, each written "name=pattern", e.g. a 6-digit code in a message body
Clear enough to supply this argument.
timeout
How long to block, in seconds, before returning status "timeout"
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"event": {
"type": "object",
"properties": {},
"description": "The matched account event; shape varies by event type",
"additionalProperties": {}
},
"status": {
"type": "string",
"description": "\"timeout\" when no event arrived in time"
},
"waitedSeconds": {
"type": "number",
"description": "Seconds waited before timing out"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "wait_for_event",
"title": "Wait For Event",
"description": "Block until a matching account event arrives (e.g. an inbound SMS, or a call ending). Only events that arrive after the call starts count. Returns { event } with the event's type and data, or { status: \"timeout\", waitedSeconds } when none arrived in time: call it again to keep waiting.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"eventType"
],
"properties": {
"field": {
"type": "array",
"items": {
"type": "string"
},
"description": "Only match events whose field equals a value, each written \"name=value\", e.g. \"callId=...\" from place_call. Every filter must match"
},
"regex": {
"type": "array",
"items": {
"type": "string"
},
"description": "Only match events whose field matches a regular expression, each written \"name=pattern\", e.g. a 6-digit code in a message body"
},
"timeout": {
"type": "integer",
"default": 30,
"maximum": 60,
"minimum": 1,
"description": "How long to block, in seconds, before returning status \"timeout\""
},
"eventType": {
"type": "string",
"minLength": 1,
"description": "Which event to wait for, e.g. message.received for an inbound message or call.ended for a finished call"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"event": {
"type": "object",
"properties": {},
"description": "The matched account event; shape varies by event type",
"additionalProperties": {}
},
"status": {
"type": "string",
"description": "\"timeout\" when no event arrived in time"
},
"waitedSeconds": {
"type": "number",
"description": "Seconds waited before timing out"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description88/100
List the account's policies. Today every policy is an allow list for inbound calls: only callers on it ring through, and other inbound calls are declined. Each item has entryCount but no entries; use get_policy for the callers.
The description explains the capability, when to use it, and what to expect.
Inputs100/100
This tool declares no inputs, so argument descriptions are not needed.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policies"
],
"properties": {
"policies": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "list_policies",
"title": "List Policies",
"description": "List the account's policies. Today every policy is an allow list for inbound calls: only callers on it ring through, and other inbound calls are declined. Each item has entryCount but no entries; use get_policy for the callers.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policies"
],
"properties": {
"policies": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description89/100
Fetch one allow list with every caller on it: its name, scope and the numbers it covers, whether it is enabled, each caller's number and label, and how many inbound calls it declined in the last 7 days.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
policyId
The policy id (from list_policies)
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
Align the output schema with the tool’s documented result.
View output schema
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "get_policy",
"title": "Get Policy",
"description": "Fetch one allow list with every caller on it: its name, scope and the numbers it covers, whether it is enabled, each caller's number and label, and how many inbound calls it declined in the last 7 days.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policyId"
],
"properties": {
"policyId": {
"type": "string",
"minLength": 1,
"description": "The policy id (from list_policies)"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": true,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description96/100
Create an allow list for inbound calls: callers not on it are declined before the call connects, recorded as a call with status.terminationType "blocked". Outbound calls and messages are not affected. Returns the new allow list, whose id is what get_policy, update_policy and delete_policy take.
Describe the returned information so the agent knows whether it will answer the request.
Inputs88/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
name
A label to tell the list apart, e.g. "Known customers"
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
scope
Which numbers the list screens. "account": every number on the account, including ones added later; an account can have only one such list. "numbers": only the numbers in numberIds, where it takes over from the account-wide list; each number can have only one list of its own.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
numberIds
Ids of the numbers this list screens (from list_numbers); needed when scope is "numbers". Only numbers that can take calls can be covered.
Clear enough to supply this argument.
enabled
Whether the list starts screening calls immediately; pass false to create it paused
Clear enough to supply this argument.
entries › items › number
Phone number of a caller to let through, in E.164 form; a 10-digit number is read as US
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
entries › items › label
Who this caller is, e.g. "Mom" or "Front desk", shown back to you in get_policy
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "create_policy",
"title": "Create Policy",
"description": "Create an allow list for inbound calls: callers not on it are declined before the call connects, recorded as a call with status.terminationType \"blocked\". Outbound calls and messages are not affected. Returns the new allow list, whose id is what get_policy, update_policy and delete_policy take.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"name",
"scope"
],
"properties": {
"name": {
"type": "string",
"maxLength": 60,
"minLength": 1,
"description": "A label to tell the list apart, e.g. \"Known customers\""
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "Which numbers the list screens. \"account\": every number on the account, including ones added later; an account can have only one such list. \"numbers\": only the numbers in numberIds, where it takes over from the account-wide list; each number can have only one list of its own."
},
"enabled": {
"type": "boolean",
"description": "Whether the list starts screening calls immediately; pass false to create it paused"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string",
"maxLength": 60
},
{
"type": "null"
}
],
"description": "Who this caller is, e.g. \"Mom\" or \"Front desk\", shown back to you in get_policy"
},
"number": {
"type": "string",
"minLength": 1,
"description": "Phone number of a caller to let through, in E.164 form; a 10-digit number is read as US"
}
}
},
"maxItems": 10000,
"description": "The callers allowed through. An enabled list with no callers declines every inbound call on its numbers."
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers this list screens (from list_numbers); needed when scope is \"numbers\". Only numbers that can take calls can be covered."
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description95/100
Change an allow list's name, coverage, on/off state or callers; anything you omit stays as it is. entries replaces the WHOLE caller list, so callers you leave out are removed. Takes effect from the next inbound call. Returns the updated allow list.
Describe the returned information so the agent knows whether it will answer the request.
Inputs93/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
policyId
Id of the allow list to change, from list_policies
Clear enough to supply this argument.
name
A new label for the list; omit to keep the current one
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
scope
"account" to screen every number without a list of its own, or "numbers" to screen only numberIds (which must then be given). Omit to keep the current scope
Clear enough to supply this argument.
numberIds
Ids of the numbers the list screens (from list_numbers), replacing the current set; omit to keep it
Clear enough to supply this argument.
enabled
false pauses the list; its numbers fall back to the account-wide list, if any
Clear enough to supply this argument.
entries › items › number
Phone number of a caller to let through, in E.164 form; a 10-digit number is read as US
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
entries › items › label
Who this caller is, e.g. "Mom" or "Front desk", shown back to you in get_policy
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "update_policy",
"title": "Update Policy",
"description": "Change an allow list's name, coverage, on/off state or callers; anything you omit stays as it is. entries replaces the WHOLE caller list, so callers you leave out are removed. Takes effect from the next inbound call. Returns the updated allow list.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policyId"
],
"properties": {
"name": {
"type": "string",
"maxLength": 60,
"minLength": 1,
"description": "A new label for the list; omit to keep the current one"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" to screen every number without a list of its own, or \"numbers\" to screen only numberIds (which must then be given). Omit to keep the current scope"
},
"enabled": {
"type": "boolean",
"description": "false pauses the list; its numbers fall back to the account-wide list, if any"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string",
"maxLength": 60
},
{
"type": "null"
}
],
"description": "Who this caller is, e.g. \"Mom\" or \"Front desk\", shown back to you in get_policy"
},
"number": {
"type": "string",
"minLength": 1,
"description": "Phone number of a caller to let through, in E.164 form; a 10-digit number is read as US"
}
}
},
"maxItems": 10000,
"description": "Replaces the WHOLE list of callers; to change a few use add_policy_entries / remove_policy_entries"
},
"policyId": {
"type": "string",
"minLength": 1,
"description": "Id of the allow list to change, from list_policies"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers the list screens (from list_numbers), replacing the current set; omit to keep it"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description99/100
Permanently delete an allow list and every caller on it; it cannot be restored. From the next inbound call, the numbers it covered fall back to the account-wide list if there is one, and otherwise every caller rings through. To stop screening for a while instead, use update_policy with enabled false. Returns deleted true with the policyId.
The description explains the capability, when to use it, and what to expect.
Inputs98/100
Input documentation gives an agent enough context to populate the arguments.
policyId
Id of the allow list to delete, from list_policies
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"deleted",
"policyId"
],
"properties": {
"deleted": {
"type": "boolean",
"const": true,
"description": "Always true: the allow list no longer exists"
},
"policyId": {
"type": "string",
"description": "Id of the allow list that was deleted"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "delete_policy",
"title": "Delete Policy",
"description": "Permanently delete an allow list and every caller on it; it cannot be restored. From the next inbound call, the numbers it covered fall back to the account-wide list if there is one, and otherwise every caller rings through. To stop screening for a while instead, use update_policy with enabled false. Returns deleted true with the policyId.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policyId"
],
"properties": {
"policyId": {
"type": "string",
"minLength": 1,
"description": "Id of the allow list to delete, from list_policies"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"deleted",
"policyId"
],
"properties": {
"deleted": {
"type": "boolean",
"const": true,
"description": "Always true: the allow list no longer exists"
},
"policyId": {
"type": "string",
"description": "Id of the allow list that was deleted"
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description94/100
Add callers to an existing allow list, leaving the callers already on it in place — no need to resend the whole list. A caller who is already on it keeps a single entry and takes the new label. Takes effect from the next inbound call. Returns the updated allow list with every caller on it.
Describe the returned information so the agent knows whether it will answer the request.
Inputs83/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
policyId
Id of the allow list to add callers to, from list_policies or create_policy
Clear enough to supply this argument.
entries › items › number
Phone number of a caller to let through, in E.164 form; a 10-digit number is read as US
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
entries › items › label
Who this caller is, e.g. "Mom" or "Front desk", shown back to you in get_policy
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "add_policy_entries",
"title": "Add Policy Entries",
"description": "Add callers to an existing allow list, leaving the callers already on it in place — no need to resend the whole list. A caller who is already on it keeps a single entry and takes the new label. Takes effect from the next inbound call. Returns the updated allow list with every caller on it.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policyId",
"entries"
],
"properties": {
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string",
"maxLength": 60
},
{
"type": "null"
}
],
"description": "Who this caller is, e.g. \"Mom\" or \"Front desk\", shown back to you in get_policy"
},
"number": {
"type": "string",
"minLength": 1,
"description": "Phone number of a caller to let through, in E.164 form; a 10-digit number is read as US"
}
}
},
"minItems": 1,
"description": "Callers to allow; one already on the list takes the given label"
},
"policyId": {
"type": "string",
"minLength": 1,
"description": "Id of the allow list to add callers to, from list_policies or create_policy"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description90/100
Take callers off an allow list. From the next inbound call they are declined like any other caller who is not on it. Returns the updated allow list with the callers that remain.
Describe the returned information so the agent knows whether it will answer the request.
Inputs95/100
Input documentation gives an agent enough context to populate the arguments.
policyId
Id of the allow list to remove callers from, from list_policies
Clear enough to supply this argument.
numbers
Phone numbers of the callers to take off the list; numbers that are not on it are ignored
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "remove_policy_entries",
"title": "Remove Policy Entries",
"description": "Take callers off an allow list. From the next inbound call they are declined like any other caller who is not on it. Returns the updated allow list with the callers that remain.",
"inputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policyId",
"numbers"
],
"properties": {
"numbers": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"minItems": 1,
"description": "Phone numbers of the callers to take off the list; numbers that are not on it are ignored"
},
"policyId": {
"type": "string",
"minLength": 1,
"description": "Id of the allow list to remove callers from, from list_policies"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"policy"
],
"properties": {
"policy": {
"type": "object",
"required": [
"id",
"name",
"type",
"appliesTo",
"scope",
"numberIds",
"enabled",
"entryCount",
"blockedCallsLast7Days",
"createdAt",
"updatedAt"
],
"properties": {
"id": {
"type": "string",
"description": "Policy id"
},
"name": {
"type": "string",
"description": "The list's label"
},
"type": {
"type": "string",
"const": "allow_list",
"description": "The kind of policy; every policy is an allow list today"
},
"scope": {
"enum": [
"account",
"numbers"
],
"type": "string",
"description": "\"account\" covers every number without a list of its own; \"numbers\" covers numberIds and replaces the account list on them"
},
"enabled": {
"type": "boolean",
"description": "A disabled policy counts as if it weren't there"
},
"entries": {
"type": "array",
"items": {
"type": "object",
"required": [
"number",
"label"
],
"properties": {
"label": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Who the caller is, or null when no label was given"
},
"number": {
"type": "string",
"description": "The caller's phone number, E.164"
}
},
"additionalProperties": false
},
"description": "Every caller on the list; left out of list_policies, which reports entryCount only"
},
"appliesTo": {
"type": "string",
"const": "inbound_calls",
"description": "What the policy screens; inbound calls today"
},
"createdAt": {
"type": "string",
"description": "When the list was created, ISO-8601"
},
"numberIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Ids of the numbers covered when scope is \"numbers\"; empty for an account-wide list"
},
"updatedAt": {
"type": "string",
"description": "When the list last changed, ISO-8601"
},
"entryCount": {
"type": "number",
"description": "How many callers are on the list"
},
"blockedCallsLast7Days": {
"type": "number",
"description": "Inbound calls this policy declined in the last 7 days"
}
},
"additionalProperties": {}
}
},
"additionalProperties": false
},
"annotations": {
"readOnlyHint": false,
"openWorldHint": true,
"idempotentHint": true,
"destructiveHint": true
}
}
Loading score analytics…
Put your MCP to the test
A few tasks worth trying with this server, chosen from its reviewed tools. These have not been run.
01List MessagesList recent messages on your account, newest first (up to 100).
02Look Up A NumberCheck what channels a phone number can receive on, before sending to it.
03List the phone numbers on your account
04Get CallFetch a single call: status, duration, and transcript.
05Get account statusCheck whether the account is ready to send messages and place calls.
Want to run these tasks against your MCP server with us?
Dial setup, tools, quality score, publishing readiness, and alternatives.
How do I add Dial to Claude, ChatGPT, or Cursor?
Add Dial to Claude, ChatGPT, or Cursor by connecting its remote MCP URL in each client's MCP settings. In Claude, add a custom connector. In ChatGPT, add an MCP app. In Cursor, add an MCP server under Settings. The MCP URL is https://getdial.ai/mcp.
Is Dial MCP ready for ChatGPT and Claude?
As of October 26, 2026, Dial scores 100/100 on Manufact's MCP publishing checklist. The scan meets ChatGPT publishing checks and meets Claude publishing checks. Passing these checks does not guarantee marketplace approval.
How clear is Dial's MCP tool documentation?
As of October 26, 2026, Dial scores 94/100 in Manufact's review of tool titles, descriptions, input and output schemas, and behavior annotations. The review covers 25 of 25 discovered tools. The quality score measures documentation clarity; runtime behavior and marketplace readiness require separate checks.
What are the best alternatives to Dial?
As of October 26, 2026, alternatives to Dial in communication on the Manufact MCP leaderboard include Primitive Email, Granola, AgentLine: Phone Numbers for AI Agents, JooVoice, and inkbox.
Not your MCP?
Review any public or OAuth MCP server for protocol compatibility, tool documentation, and ChatGPT and Claude publishing readiness. No account required.