Skip to content

Phone & Voice API

AI-powered outbound voice calls (Bland.ai) and wallet-owned phone numbers (Twilio). Make conversational AI calls with real-time transcripts, buy/manage dedicated US/CA numbers, and perform carrier/fraud lookups. Outbound only, no SMS.

Base URLs:

  • Phone numbers & lookups: https://api.jarvisclaw.ai/v1/marketplace/phone
  • Voice calls: https://api.jarvisclaw.ai/v1/marketplace/voice

Authentication

Both methods are supported — all requests settle via x402 on-chain:

MethodHeaderDescription
API KeyAuthorization: Bearer sk-...Platform signs x402 from your HD wallet automatically
Private Key (x402)Automatic via SDKAgent signs x402 directly from its own wallet

See Agent Payments (x402) for full details on how both methods work.

Pricing

Reference rates per request. For the exact charge on a given call, read price.amount in the response.

EndpointPriceDescription
POST /v1/marketplace/voice/call$0.54/callInitiate AI voice call (max 30 min)
POST /v1/marketplace/voice/call/transfer$0.54/callTransfer an active call
POST /v1/marketplace/voice/call/end$0.001/requestEnd an active call
GET /v1/marketplace/voice/call$0.542/requestRetrieve call status/transcript
POST /v1/marketplace/phone/numbers/buy$5.002/numberLease a phone number (30-day)
POST /v1/marketplace/phone/numbers/renew$5.00/numberExtend lease by 30 days
POST /v1/marketplace/phone/numbers/list$0.003/requestList owned numbers
POST /v1/marketplace/phone/numbers/release$0.003/requestRelease a number
POST /v1/marketplace/phone/lookup$0.01/requestCarrier identification
POST /v1/marketplace/phone/lookup/fraud$0.05/requestFraud risk + SIM swap detection
POST /v1/marketplace/phone/sms/send$0.05/messageSend an SMS

Every phone endpoint is POST, and reads are not free

numbers/list and numbers/release are POST and both cost $0.003 — the GET forms return "Unknown phone endpoint" upstream. Retrieving a call transcript via GET .../voice/call is billed at $0.542, the same as placing the call, because that is what upstream charges.

Endpoints

POST /v1/marketplace/voice/call

Initiate an outbound AI voice call. The AI agent handles the conversation using the provided task/system prompt. Powered by Bland.ai. Requires a leased phone number as caller ID.

Quick Start Cost

First call runs about $5.54: $5.002 for a 30-day number plus $0.54 for the call. Each transcript read adds a further $0.542, so poll deliberately rather than in a tight loop.

Parameters

ParameterTypeRequiredDescription
tostringYesDestination phone number (E.164 format, e.g., +14155551234)
fromstringYesCaller ID — must be a number leased via /phone/numbers/buy and owned by your wallet
taskstringYesInstructions for the AI voice agent (plain English system prompt)
voicestringNoVoice preset (e.g., maya, josh). Default: maya
max_durationintegerNoMax call duration in minutes. Default: 5, max: 30
first_sentencestringNoOverride the AI's opening line
wait_for_greetingbooleanNoWait for callee to speak first. Default: true

Request Example

json
{
  "to": "+12025551234",
  "from": "+14155551234",
  "task": "Hi, this is Maya from JarvisClaw. Confirm tomorrow at 3pm and ask the recipient to press 1 to confirm or 2 to reschedule. Keep it under 60 seconds.",
  "voice": "maya",
  "max_duration": 5
}

Response Example

json
{
  "call_id": "0721a3f8-9ae6-...",
  "status": "success",
  "poll_url": "https://api.jarvisclaw.ai/v1/marketplace/voice/call/0721a3f8-...",
  "message": "Call initiated. Poll poll_url for status, transcript, and recording."
}

Error: No Owned Number

If from is not a number owned by your wallet:

json
{
  "error": "Forbidden",
  "message": "You do not own +14155551234. Your wallet (0x...) doesn't own any active numbers. Buy one at POST /api/v1/phone/numbers/buy."
}

GET /v1/marketplace/voice/call/:call_id

Retrieve the full transcript, status, and metadata for a completed call. No payment required.

Parameters

ParameterTypeRequiredDescription
call_idstringYesCall ID (path parameter)

Response Example

json
{
  "status": "completed",
  "call_length": 0.75,
  "answered_by": "human",
  "from": "+14155551234",
  "to": "+12025551234",
  "concatenated_transcript": "AI: Hi, this is Maya from JarvisClaw...\nHuman: Yes, hello?...",
  "recording_url": "https://..."
}

Call statuses: initiated, ringing, in_progress, completed, failed, no_answer, busy


POST /lookup

Identify the carrier and line type for a phone number.

Parameters

ParameterTypeRequiredDescription
phoneNumberstringYesPhone number in E.164 format

Request Example

json
{
  "phoneNumber": "+14155551234"
}

Response Example

json
{
  "phone_number": "+14155551234",
  "calling_country_code": "1",
  "country_code": "US",
  "national_format": "(415) 555-1234",
  "line_type_intelligence": {
    "carrier_name": "T-Mobile",
    "type": "mobile",
    "mobile_country_code": "310",
    "mobile_network_code": "260"
  },
  "caller_name": null,
  "sim_swap": null
}

POST /lookup/fraud

Assess fraud risk and identify suspicious indicators for a phone number.

Parameters

ParameterTypeRequiredDescription
phoneNumberstringYesPhone number in E.164 format

Request Example

json
{
  "phoneNumber": "+14155551234"
}

Response Example

json
{
  "phone_number": "+14155551234",
  "calling_country_code": "1",
  "country_code": "US",
  "national_format": "(415) 555-1234",
  "line_type_intelligence": {
    "carrier_name": "T-Mobile",
    "type": "mobile"
  },
  "call_forwarding": {
    "call_forwarding_enabled": false
  },
  "sim_swap": {
    "last_sim_swap": null,
    "swapped_in_period": false
  }
}

Risk levels: low (0-25), medium (26-50), high (51-75), critical (76-100)


POST /numbers/buy

Lease a dedicated phone number for use as a caller ID on outbound calls.

Parameters

ParameterTypeRequiredDescription
countrystringYesISO country code (US or CA)
areaCodestringNoPreferred area code (e.g., 415)

Request Example

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

Response Example

json
{
  "phone_number": "+14155559876",
  "expires_at": "2026-07-20T00:00:00.000Z",
  "chain": "base",
  "message": "Number provisioned for 30 days..."
}

POST /numbers/list

List all phone numbers currently leased to your account.

Request Example

json
{}

Response Example

json
{
  "numbers": [],
  "count": 0
}

Errors

HTTP CodeError CodeDescription
400invalid_phone_numberPhone number is not valid E.164 format
404number_not_ownedThe from number is not leased to your account
404call_not_foundNo call found with the specified call_id
409number_unavailableRequested number or area code has no availability
500call_failedVoice call could not be connected (carrier/network issue)

Error Response Format

json
{
  "error": {
    "code": "invalid_phone_number",
    "message": "The phone number '+1415555' is not valid E.164 format. Expected format: +14155551234"
  }
}

Code Examples

bash
# 1. Buy a phone number ($5.00)
curl -X POST https://api.jarvisclaw.ai/v1/marketplace/phone/numbers/buy \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"country": "US", "areaCode": "415"}'

# 2. Initiate a voice call ($0.54) — note: /v1/marketplace/voice/ (separate service)
curl -X POST https://api.jarvisclaw.ai/v1/marketplace/voice/call \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+12025551234",
    "from": "+14155551234",
    "task": "Confirm the dental appointment for Tuesday at 2 PM. Keep it under 60 seconds.",
    "voice": "maya",
    "max_duration": 5
  }'

# 3. Get call transcript ($0.542/request)
curl https://api.jarvisclaw.ai/v1/marketplace/voice/call/0721a3f8-9ae6-... \
  -H "Authorization: Bearer sk-your-api-key"

# Carrier lookup
curl -X POST https://api.jarvisclaw.ai/v1/marketplace/phone/lookup \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"phoneNumber": "+14155551234"}'

# Fraud risk check
curl -X POST https://api.jarvisclaw.ai/v1/marketplace/phone/lookup/fraud \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"phoneNumber": "+14155551234"}'

# List owned numbers
curl -X POST https://api.jarvisclaw.ai/v1/marketplace/phone/numbers/list \
  -H "Authorization: Bearer sk-your-api-key" \
  -H "Content-Type: application/json" \
  -d '{}'
python
import requests

PHONE_BASE = "https://api.jarvisclaw.ai/v1/marketplace/phone"
VOICE_BASE = "https://api.jarvisclaw.ai/v1/marketplace/voice"
HEADERS = {
    "Authorization": "Bearer sk-your-api-key",
    "Content-Type": "application/json",
}

# 1. Buy a number
resp = requests.post(f"{PHONE_BASE}/numbers/buy", headers=HEADERS, json={
    "country": "US",
    "areaCode": "415",
})
number = resp.json()
print(f"Leased: {number['phone_number']}, expires: {number['expires_at']}")

# 2. Initiate an AI voice call (separate service: /marketplace/voice)
resp = requests.post(f"{VOICE_BASE}/call", headers=HEADERS, json={
    "to": "+12025551234",
    "from": number["phone_number"],
    "task": "Confirm the dental appointment for Tuesday at 2 PM.",
    "voice": "maya",
    "max_duration": 5,
})
call = resp.json()
print(f"Call initiated: {call['call_id']}")

# 3. Poll transcript ($0.542/request)
resp = requests.get(f"{VOICE_BASE}/call/{call['call_id']}", headers=HEADERS)
result = resp.json()
print(f"Status: {result['status']}, Duration: {result.get('call_length')} min")
print(f"Answered by: {result.get('answered_by')}")
print(f"Transcript: {result.get('concatenated_transcript', '')[:200]}")

# Carrier lookup
resp = requests.post(f"{PHONE_BASE}/lookup", headers=HEADERS, json={
    "phoneNumber": "+14155551234",
})
info = resp.json()
lt = info.get("line_type_intelligence", {})
print(f"Carrier: {lt.get('carrier_name')}, Type: {lt.get('type')}")

# Fraud risk assessment
resp = requests.post(f"{PHONE_BASE}/lookup/fraud", headers=HEADERS, json={
    "phoneNumber": "+14155551234",
})
risk = resp.json()
print(f"SIM swap: {risk.get('sim_swap')}")
print(f"Call forwarding: {risk.get('call_forwarding')}")

# List owned numbers
resp = requests.post(f"{PHONE_BASE}/numbers/list", headers=HEADERS, json={})
print(f"Numbers: {resp.json()}")
python
from jarvisclaw import MarketplaceClient

# x402 Agent wallet — pays per-call via USDC
client = MarketplaceClient(private_key="0x<evm-private-key>")

# 1. Buy a number
number = client.call("phone", "/numbers/buy", method="POST", json={
    "country": "US",
    "areaCode": "415",
})
print(f"Leased: {number['phone_number']}")

# 2. Voice call (note: separate 'voice' service)
call = client.call("voice", "/call", method="POST", json={
    "to": "+12025551234",
    "from": number["phone_number"],
    "task": "Confirm appointment for Tuesday at 2 PM.",
    "voice": "maya",
    "max_duration": 5,
})
print(f"Call initiated: {call['call_id']}")

# 3. Get transcript ($0.542/request)
transcript = client.call("voice", f"/call/{call['call_id']}")
print(f"Status: {transcript['status']}")
print(f"Transcript: {transcript.get('concatenated_transcript', '')[:200]}")

# Carrier lookup
info = client.call("phone", "/lookup", method="POST", json={
    "phoneNumber": "+14155551234",
})
lt = info.get("line_type_intelligence", {})
print(f"Carrier: {lt.get('carrier_name')}, Type: {lt.get('type')}")
print(f"Risk: {risk['risk_level']} (score: {risk['risk_score']})")

# Buy a number
number = client.call("phone", "/numbers/buy", method="POST", json={
    "country": "US",
    "area_code": "415",
})
print(f"Leased: {number['number']}")

# List owned numbers
numbers = client.call("phone", "/numbers/list", method="POST", json={})
for num in numbers["numbers"]:
    print(f"  {num['number']} — expires {num['expires_at']}")
go
package main

import (
    "context"
    "fmt"
    jc "github.com/api-jarvisclaw/go-sdk/v2"
)

func main() {
    ctx := context.Background()
    mc, _ := jc.NewMarketplaceClient(jc.WithAPIKey("sk-your-api-key"))

    // 1. Buy a number
    number, _ := mc.Post(ctx, "phone", "/numbers/buy", map[string]interface{}{
        "country":  "US",
        "areaCode": "415",
    })
    fmt.Printf("Leased: %s\n", number["phone_number"])

    // 2. Voice call (note: 'voice' service, not 'phone')
    call, _ := mc.Post(ctx, "voice", "/call", map[string]interface{}{
        "to":           "+12025551234",
        "from":         number["phone_number"],
        "task":         "Confirm dental appointment Tuesday at 2 PM.",
        "voice":        "maya",
        "max_duration": 5,
    })
    fmt.Printf("Call initiated: %s\n", call["call_id"])

    // 3. Get transcript ($0.542/request)
    transcript, _ := mc.Get(ctx, "voice", fmt.Sprintf("/call/%s", call["call_id"]))
    fmt.Printf("Status: %s, Answered by: %s\n", transcript["status"], transcript["answered_by"])

    // Carrier lookup
    info, _ := mc.Post(ctx, "phone", "/lookup", map[string]interface{}{
        "phoneNumber": "+14155551234",
    })
    fmt.Printf("Country: %s\n", info["country_code"])
}
go
package main

import (
    "context"
    "fmt"
    jc "github.com/api-jarvisclaw/go-sdk/v2"
)

func main() {
    ctx := context.Background()

    // x402 Agent wallet — pays per-call via USDC on Base
    mc, _ := jc.NewMarketplaceClient(jc.WithPrivateKey("0x<evm-private-key>"))

    // 1. Buy a number
    number, _ := mc.Post(ctx, "phone", "/numbers/buy", map[string]interface{}{
        "country":  "US",
        "areaCode": "415",
    })
    fmt.Printf("Leased: %s\n", number["phone_number"])

    // 2. Voice call ('voice' service)
    call, _ := mc.Post(ctx, "voice", "/call", map[string]interface{}{
        "to":           "+12025551234",
        "from":         number["phone_number"],
        "task":         "Confirm dental appointment Tuesday at 2 PM.",
        "voice":        "maya",
        "max_duration": 5,
    })
    fmt.Printf("Call initiated: %s\n", call["call_id"])

    // 3. Get transcript ($0.542/request)
    transcript, _ := mc.Get(ctx, "voice", fmt.Sprintf("/call/%s", call["call_id"]))
    fmt.Printf("Status: %s\n", transcript["status"])
}

Limitations

  • Outbound only — inbound call handling and receiving calls is not supported
  • No SMS — phone numbers are voice-only, SMS is not available
  • 30-minute max call duration — calls are automatically terminated at 1800 seconds
  • US/CA default — numbers outside US and Canada require KYC verification
  • English only — voice AI currently supports English language conversations only
  • 30-day lease — phone numbers auto-renew monthly unless cancelled
  • E.164 format required — all phone numbers must include country code (e.g., +14155551234)
  • Caller ID — the from field must be a number you've leased, or it will be omitted (calls show as unknown)