API
Phone
Numbers and calls. Availability depends on the voice-call stack.
Hawkeye does not buy PSTN numbers and does not add a carrier binding. You attach an E.164 number you already control, or you provision a bridge. Outbound calls use the same voice stack as the product, including the fast prep path. Audio belongs on the provider session (signed_url or a WebRTC token). Binary audio on the Hawkeye websocket is refused.
Numbers
- GET /api/partner/v1/phone-numbers lists numbers on the account.
- POST with action attach and an e164 field (leading +, then 8 to 15 digits) attaches a number you control.
- POST with action provision, or with no number, creates a bridge. The status is bridge.
- DELETE /api/partner/v1/phone-numbers/{id} detaches one. The id looks like num_ plus 12 hex characters.
Calls
- POST /api/partner/v1/calls with agent_id and to starts an outbound call.
- POST /api/partner/v1/phone/inbound tells Hawkeye an inbound call arrived.
- POST /api/partner/v1/calls/{id}/turns with a text field sends one utterance and returns the spoken reply.
- GET /api/partner/v1/voice/stream is the websocket. Text frames are
{"type":"utterance","call_id":"...","text":"..."}. A plain GET without an upgrade answers 400 upgrade. - GET /api/partner/v1/calls/{id} returns the transcript and summary.
- POST /api/partner/v1/calls/{id}/end hangs up.
curl -s https://hawkeye.brownhawke.engineering/api/partner/v1/calls \
-H "Authorization: Bearer $HAWKEYE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"agent_id":"receptionist","to":"+441234567890"}'await fetch(process.env.HAWKEYE_HOST + "/api/partner/v1/calls", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.HAWKEYE_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ agent_id: "receptionist", to: "+441234567890" }),
});import os, requests
r = requests.post(
os.environ["HAWKEYE_HOST"] + "/api/partner/v1/calls",
headers={"Authorization": "Bearer " + os.environ["HAWKEYE_API_KEY"]},
json={"agent_id": "receptionist", "to": "+441234567890"},
timeout=60,
)
print(r.status_code, r.json())The voice scope is required. A key without it receives 403 scope_denied.