Tel
Package tel is the telecommunications surface: phone numbers, calls and messages, on whatever carrier the deployment is configured for.
Package tel is the telecommunications surface: phone numbers, calls and messages, on whatever carrier the deployment is configured for.
| Base URL | https://api.hanzo.ai |
| Operations | 10 |
| Auth | Authorization: Bearer $HANZO_API_KEY |
Quickstart
export HANZO_API_KEY=hk-... # console.hanzo.ai → API keysThen the first call — a read that needs nothing but the key. GET /v1/tel/calls, operation get_tel_calls:
hanzo has no subcommand for this operation — the CLI serves only what cloud's live route table confirms. Use HTTP or an SDK.
import { Configuration, TelApi } from 'hanzoai';
const api = new TelApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.getTelCalls();from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import TelApi
client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = TelApi(client).get_tel_calls()cfg := cloud.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := cloud.NewAPIClient(cfg)
resp, _, err := client.TelAPI.GetTelCalls(context.Background()).Execute()
if err != nil {
return err
}use hanzo_cloud::apis::{configuration::Configuration, tel_api};
let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();
let result = tel_api::get_tel_calls(&cfg, Default::default()).await?;import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.TelApi;
ApiClient client = new ApiClient();
client.setRequestInterceptor(b -> b.header("Authorization", "Bearer " + System.getenv("HANZO_API_KEY")));
var result = new TelApi(client).getTelCalls();The method above is the one at the current release of the document. [email protected] (PyPI) was generated from an earlier release, where this operation carried a different id, so it spells the method differently — regenerating the clients is what makes the two agree. SDKs →
curl https://api.hanzo.ai/v1/tel/calls \
-H "Authorization: Bearer $HANZO_API_KEY"The door reaches tel through the tel tool, which names its 10 operations with its own verbs — this one among them, under a name only the door declares. describe explains any of them:
curl -X POST https://api.hanzo.ai/v1/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "describe",
"arguments": {
"op": "list_tel_calls"
}
}
}'Answers 200 with object — ok.
tel
DELETE /v1/tel/calls/{id}
Ends a live call. The holding is checked before the carrier is asked, so a call belonging to another org answers 404 rather than being torn down: the carrier knows nothing about tenancy and would obey either way.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes |
GET /v1/tel/calls
Returns this org's call history, newest first as the store gives it. Org-scoped like every read here.
POST /v1/tel/calls
Dials. An agent names a Hanzo assistant to answer it; the call is
refused up front when no assistant plane is configured, because a call that
connects to silence has already cost the person who answered it.
Request body — application/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
agent | string | — | |
from | string | — | |
record | boolean | — | Record is a per-call flag rather than a product. |
to | string | — | |
webhook | string | — |
GET /v1/tel/messages
Returns this org's message history. Org-scoped like every read here.
POST /v1/tel/messages
Sends one message. from must be a number this org holds -- the carrier
will send from any number it routes, so without that check one tenant could
originate traffic on another's number, and the bill and the reputation would
follow the number rather than the sender.
Request body — application/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
from | string | — | |
media | string[] | — | |
text | string | — | |
to | string | — |
DELETE /v1/tel/numbers/{id}
Checks the holding is THIS org's before it reaches the carrier. Without that read, an id belonging to another tenant would be released by whoever guessed it.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes |
GET /v1/tel/numbers/available
Asks the carrier what is available to buy. Nothing is recorded — a search is not a holding, and treating it as one is how inventory leaks.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
Country | query | string | — | |
Area | query | string | — | |
Type | query | string | — | |
Limit | query | integer | — |
GET /v1/tel/numbers
Returns the numbers this org holds. Scoped to the caller's org, so a number bought by one tenant is invisible to every other.
POST /v1/tel/numbers
Provisions with the carrier FIRST and records second. The other order records a holding that may not exist, and a number the platform believes it owns but cannot use is worse than one it failed to buy.
Request body — application/json (required)
| Field | Type | Required | Description |
|---|---|---|---|
e164 | string | — |
GET /v1/tel/summary
Counts what this org holds and has used -- numbers, calls, messages. The one read the dashboard makes, so it is three counts rather than three lists.
How is this guide?