phone ยท git:20260905.317761a ยท 2026-09-05 ยท sha256 32f3d6df1039eb96

phone git:20260905.317761aA

Immutable. This exact content is served forever at /api/v1/blob/32f3d6df1039eb96.

---
name: phone
description: Verify phone numbers (carrier + SIM-swap fraud signals) and place AI-powered outbound voice calls via BlockRun's gateway (Twilio + Bland.ai). Trigger when the user asks to look up a number, check fraud risk, buy/rent a phone number, or place an AI voice call. Payment is automatic via x402 from the wallet.
triggers:
  - "blockrun phone"
  - "blockrun voice"
  - "voice call"
  - "outbound call"
  - "ai phone call"
  - "ai voice call"
  - "phone number lookup"
  - "phone number verification"
  - "carrier lookup"
  - "sim swap"
  - "sim-swap detection"
  - "phone fraud check"
  - "buy phone number"
  - "rent phone number"
  - "twilio number"
  - "bland.ai"
  - "blockrun bland"
metadata: { "openclaw": { "emoji": "๐Ÿ“ž", "requires": { "config": ["models.providers.blockrun"] } } }
---

# Phone & Voice

Phone-number intelligence (Twilio Lookup) and AI-powered outbound voice calls (Bland.ai) through ClawRouter's local proxy. Payment is automatic via x402 from the user's BlockRun wallet.

**Shortcuts:**

- Slash: `/cr-call +1<E.164> "<task>" [--voice nat] [--max-duration 5] [--from +1<owned-number>] [--language en-US]`
- CLI: `clawrouter phone numbers list/buy/renew/release`, `clawrouter phone lookup <+E.164>`, `clawrouter phone fraud <+E.164>`
- Partner tools (LLM-callable): `blockrun_phone_lookup`, `blockrun_phone_lookup_fraud`, `blockrun_phone_numbers_buy/renew/list/release`, `blockrun_voice_call`, `blockrun_voice_status`

> **โš ๏ธ Real-world side effects.** `blockrun_voice_call` places a real outbound phone call to a real number. Only invoke when the user has explicitly asked for a call to be placed. Server enforces an emergency-number blocklist.

---

## Phone Number Intelligence (Twilio)

### Carrier + Line Type Lookup โ€” `$0.01`

POST to `http://localhost:8402/v1/phone/lookup`:

```json
{ "phoneNumber": "+14155552671" }
```

Returns carrier name, line type (`mobile` / `landline` / `voip`), country, mobile country/network codes. Use to verify whether a number is reachable, detect VoIP/spam patterns, or route SMS appropriately.

### Fraud Risk Check โ€” `$0.05`

POST to `http://localhost:8402/v1/phone/lookup/fraud`:

```json
{ "phoneNumber": "+14155552671" }
```

Adds SIM-swap recency and call-forwarding signals on top of carrier+line type. **Run this before sending sensitive SMS codes or initiating account-recovery flows** โ€” a number flagged for recent SIM swap is high-risk for account takeover.

---

## Wallet-Owned Phone Numbers

Numbers are leased for 30 days, bound to the wallet's payer address. Use one as the `from` parameter of `voice_call` to present a stable caller ID.

### Buy โ€” `$5.00 / 30 days`

POST to `http://localhost:8402/v1/phone/numbers/buy`:

```json
{ "country": "US", "areaCode": "415" }
```

`country` is `"US"` or `"CA"`. `areaCode` is optional (3-digit, best-effort match). Returns `{ phone_number, expires_at, chain }`.

### Renew โ€” `$5.00 / +30 days`

POST to `http://localhost:8402/v1/phone/numbers/renew`:

```json
{ "phoneNumber": "+14155551234" }
```

Run before the existing lease expires. Numbers not renewed are released back to the pool.

### List โ€” `$0.001`

POST to `http://localhost:8402/v1/phone/numbers/list` with an empty body. Returns an array of `{ phone_number, expires_at, country, chain }`. The CLI surfaces this as a human table with renew-soon warnings:

```
$ clawrouter phone numbers list

Active numbers (2):
  +14155551234   US   expires 2026-06-12 (in 27d)
  +12135555678   US   expires 2026-05-18 (in 2d) โš  renew soon
```

### Release โ€” free

POST to `http://localhost:8402/v1/phone/numbers/release`:

```json
{ "phoneNumber": "+14155551234" }
```

No refund. Use only when the user has explicitly asked to give up the number.

---

## AI Voice Call (Bland.ai)

### Place a Call โ€” `$0.54 flat (up to 30 min)`

POST to `http://localhost:8402/v1/voice/call`:

```json
{
  "to": "+14155552671",
  "task": "Call and confirm the 3pm Thursday meeting; reschedule if they can't make it.",
  "voice": "nat",
  "max_duration": 5,
  "from": "+14155551234",
  "language": "en-US"
}
```

**Required:** `to` (E.164), `task` (free-form natural language โ€” what the AI should say or accomplish).

**Optional:**

| Field          | Default     | Notes                                                                                                                                            |
| -------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `voice`        | `nat`       | Presets: `nat`, `josh`, `maya`, `june`, `paige`, `derek`, `florian`. Or a custom Bland voice ID.                                                 |
| `max_duration` | `5`         | Maximum minutes (1โ€“30). Price is flat $0.54 regardless of actual duration.                                                                       |
| `from`         | auto-picked | Must be a wallet-owned number from `phone_numbers_list`. If omitted, server auto-picks from wallet's active numbers โ€” see auto-pick rules below. |
| `language`     | `en-US`     | Any spoken-language ISO code, e.g. `es-ES`, `zh-CN`, `de-DE`.                                                                                    |

**`from` auto-pick rules** (server-side, after payment verification):

| Wallet active numbers | Behavior                                                                                                  |
| --------------------- | --------------------------------------------------------------------------------------------------------- |
| 0                     | `403 no_active_number` โ€” response includes `buy_endpoint` + marketplace URL so caller can provision first |
| Exactly 1             | Auto-used as caller ID                                                                                    |
| 2+                    | `400 ambiguous_from` โ€” response lists all active numbers; retry with `from` set explicitly                |

If an explicit `from` is supplied but the wallet doesn't own it, the response is `403` with a clear ownership-mismatch message (no charge taken when validation fails).

**Response is fire-and-forget:**

```json
{
  "call_id": "call_abc123",
  "poll_url": "/v1/voice/call/call_abc123",
  "status": "queued"
}
```

The call runs in the cloud for up to `max_duration` minutes. The HTTP response returns immediately โ€” **do not wait for the call to finish**. Poll `voice_status` every 10โ€“30s to retrieve transcript and recording when the call completes.

### Poll Status โ€” free

GET `http://localhost:8402/v1/voice/call/{call_id}`. Returns:

```json
{
  "call_id": "call_abc123",
  "status": "completed",
  "duration_seconds": 47,
  "transcript": [
    { "role": "assistant", "text": "Hi, this is calling to confirm tomorrow's 3pm meeting..." },
    { "role": "user", "text": "Yes, that still works." }
  ],
  "recording_url": "https://..."
}
```

`status` progresses through `queued` โ†’ `in_progress` โ†’ `completed` or `failed`. The transcript array and recording URL only appear on `completed`. On `failed`, an `error` field describes why.

---

## Example Agentic Flows

**Verify before texting:**

> User: "Send a verification code to +1 415 555 0123"
> Agent: First call `blockrun_phone_lookup_fraud({ phoneNumber: "+14155550123" })`. If `sim_swap.last_sim_swap` is within the past 7 days, refuse and ask the user to confirm out-of-band. Otherwise proceed.

**Appointment confirmation:**

> User: "Call my client at +1 415 555 0123 and confirm tomorrow's 3pm meeting"
> Agent: `blockrun_voice_call({ to: "+14155550123", task: "Call to confirm the 3pm Thursday meeting; if they can't make it, offer to reschedule for Friday morning.", max_duration: 5 })`. Returns `call_id`. Tell the user: "Calling now โ€” I'll have the transcript in a few minutes." Then `blockrun_voice_status({ callId })` every 30s until `completed`, then summarize the transcript.

**Acquire dedicated caller ID:**

> User: "Buy me a San Francisco number for the next 30 days"
> Agent: `blockrun_phone_numbers_buy({ country: "US", areaCode: "415" })`. Confirm the assigned number to the user and warn them: "Lease expires in 30 days, costs $5 to renew."

---

## Notes

- Payment is automatic on every call โ€” an x402 USDC micropayment from the user's wallet, or a draw on account credit if a BlockRun API key is configured.
- If a call fails with a 402, check `GET http://localhost:8402/health` and read `authMode` first: `wallet` โ†’ fund the wallet at [blockrun.ai](https://blockrun.ai); `api-key` โ†’ top up at [user.blockrun.ai/dashboard/credits](https://user.blockrun.ai/dashboard/credits).
- Number leases are bound to the **wallet's** payer address, so `phone numbers list/buy/renew/release` and the `from` parameter are wallet-mode features. In API-key mode the gateway answers those routes with a raw `402` x402 challenge (verified 2026-09-05) because there is no payer address to bind a lease to. Lookups, fraud checks and outbound calls all work on an API key; just let the server auto-pick `from`.
- Phone numbers are real, regulated resources โ€” numbers bought are reachable from any phone within ~60 seconds of purchase
- Bland.ai's emergency-number blocklist is enforced server-side; ClawRouter does not duplicate it but trusts upstream
- Recordings and transcripts are retained by Bland.ai; ClawRouter does not download them locally (returns the upstream URL)
- For programmatic polling, see the `voice_status` tool โ€” for one-off CLI checks, hit `curl http://localhost:8402/v1/voice/call/{call_id}` directly