Developers

Onyx Phone API and webhooks

Read calls, voicemails, and texts, send a text from a line, and get a webhook the moment a call ends or a text comes in. It is the same API our Zapier app uses.

Authentication

An account admin makes a key in Onyx Phone under Account → API and Zapier. The key is shown once. Send it on every request in the X-API-Key header:

curl https://www.onyxphone.io/api/v1/me \
  -H "X-API-Key: onyx_…"

A missing, wrong, or revoked key gets 401. Requests are limited to 300 a minute per key; past that you get 429. Errors are JSON: { "error": "…" }. Every time is ISO 8601 in UTC, and phone numbers are E.164 (+16125550199), each with _national and _formatted versions beside it.

Endpoints

Base URL: https://www.onyxphone.io/api/v1

RequestWhat it does
GET /meThe account the key belongs to: id, account_name, plan.
GET /linesThe account's lines: id, name, number.
GET /callsRecent calls, newest first. event picks which: call.completed (default), call.missed, or voicemail.received. Optional line_id, and limit (1–100, default 25).
GET /calls/:idOne call.
GET /messagesRecent texts, newest first. Optional direction (inbound or outbound), line_id, limit.
POST /messagesSends a text: line_id, to, body (up to 1,600 characters). Needs Texting on the plan and carrier registration approved; numbers that replied STOP are refused. Returns 201 with the text.
GET /webhooksThe account's webhook subscriptions.
POST /webhooksSubscribes: event, target_url (https, public internet), optional line_id. Returns 201 with the subscription's id and its signing secret.
DELETE /webhooks/:idUnsubscribes. Succeeds even if it was already removed.

Webhook events

EventSent when
call.completedA call ended, incoming or outgoing, answered or not.
call.missedAn incoming call ended without anyone answering.
voicemail.receivedA caller left a voicemail (sent once the transcript is ready).
message.receivedA text came in.
message.sentA text went out.

Each event is POSTed as one JSON object, the same shape GET /calls and GET /messages return. Each call or text is sent once per subscription. Answer with any 2xx. Failed deliveries are retried up to five times with growing gaps; answer 410 Gone to unsubscribe for good. A target that fails 25 deliveries in a row is removed.

A call event

{
  "id": "4821",
  "event": "call.missed",
  "occurred_at": "2026-10-23T15:04:11Z",
  "occurred_on": "2026-10-23",
  "direction": "inbound",
  "direction_label": "Inbound",
  "outcome": "voicemail",
  "outcome_label": "Voicemail",
  "summary": "Voicemail from (612) 555-0199 on Main line",
  "contact_number": "+16125550199",
  "contact_number_national": "6125550199",
  "contact_number_formatted": "(612) 555-0199",
  "caller_name": null,
  "line_id": "12",
  "line_name": "Main line",
  "line_number": "+16125550100",
  "started_at": "2026-10-23T15:03:20Z",
  "ended_at": "2026-10-23T15:04:11Z",
  "duration_seconds": 51,
  "duration_ms": 51000,
  "answered": false,
  "answered_at": null,
  "answered_by_name": null,
  "answered_by_email": null,
  "voicemail": true,
  "voicemail_seconds": 22,
  "voicemail_transcript": "Hi, this is Sam about the water heater…",
  "notes": null,
  "hubspot_call_direction": "INBOUND",
  "hubspot_call_status": "NO_ANSWER",
  "app_url": "https://www.onyxphone.io/call_logs/4821"
}

Abbreviated: from_number, to_number, and line_number also come with _national and _formatted versions. outcome is answered, voicemail, or missed for incoming calls, and completed, no_answer, busy, failed, or canceled for outgoing ones. Keys are only ever added, never renamed or removed.

Checking the signature

Every delivery carries X-Onyx-Signature: t=<unix time>,v1=<signature>. The signature is the hex HMAC-SHA256 of <t>.<raw body>, keyed with the subscription's secret. Compare it in constant time, and reject times more than five minutes old.

expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "#{t}.#{raw_body}")
ActiveSupport::SecurityUtils.secure_compare(expected, v1)

MCP server for AI assistants

Onyx Phone runs a Model Context Protocol server at POST /mcp (Streamable HTTP, JSON responses). It supports OAuth 2.1 sign-in, as the MCP authorization spec describes: discovery at /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server, dynamic client registration, and the authorization code flow with PKCE (S256). Tools you build yourself can send an API key instead, as Authorization: Bearer onyx_…. Tools: list_lines, list_calls, get_call, list_messages, send_text, and add_call_note. Each returns the same JSON as the API above, with call transcripts and summaries where a line records calls. Texts sent with send_text are billed like texts sent through the API.

Zapier

The Onyx Phone Zapier app has triggers for New Call, New Missed Call, New Voicemail, and New Text, and a Send Text action. Ready-made Zaps send missed calls and voicemails to Jobber as requests, and log calls in HubSpot and Salesforce. See integrations.