Developers
Developer guide
Put your phone system inside the software your team already uses. There are three ways to connect: embed the workspace, call the REST API, and subscribe to event webhooks.
1. Get an API key
Sign in, open Integrations, and create a key. The full key is shown once, so store it in your server's secret manager. Send it on every request as a bearer token. Never place it in browser code.
curl https://connect.breatheasy.net/api/public/v1/ping \
-H "Authorization: Bearer bec_xxxxxxxxxx.yyyyyyyyyyyyyyyyyyyy"2. Read your data
All endpoints are scoped to the account that owns the key. Optional query parameters:limit, since,until, and phone_number.
GET /api/public/v1/- key check plus the live list of endpointsGET /api/public/v1/calls- call history with outcome, duration, and AI summaryGET /api/public/v1/messages- individual text messagesGET /api/public/v1/threads- text conversationsGET /api/public/v1/threads/{id}/messages- one conversation, oldest firstGET /api/public/v1/faxes- sent and received faxesGET /api/public/v1/faxes/{id}/document- five minute download link for the PDFGET /api/public/v1/voicemails- voicemails with transcriptsGET /api/public/v1/voicemails/{id}/audio- playback link for the recordingGET /api/public/v1/extensions- your extension directoryGET /api/public/v1/numbers- your phone and fax numbers
Two small write endpoints keep your own screens in sync:POST /api/public/v1/threads/{id}/read andPOST /api/public/v1/voicemails/{id}/read.
3. Send a text or a fax
curl -X POST https://connect.breatheasy.net/api/public/v1/messages \
-H "Authorization: Bearer $BEC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from":"+15035550100","to":"+15035550111","body":"Your appointment is confirmed."}'
curl -X POST https://connect.breatheasy.net/api/public/v1/faxes \
-H "Authorization: Bearer $BEC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"from":"+15035550100","to":"+15035559999","document_name":"referral.pdf",
"document_base64":"JVBERi0xLjcK...","cover_sheet":"default"}'Keys carry read and write scopes. A read only key is rejected on these two endpoints. Requests are limited to 120 per minute per account.
4. Receive events
Add your HTTPS endpoint in Integrations and pick the events you want:call.completed, message.received,message.sent, fax.received,fax.sent, and voicemail.created. Matching a record to a patient or a claim happens on your side; every payload includes both phone numbers, the extension when we know it, and our record id.
POST /your-endpoint
x-breatheasy-event: fax.received
x-breatheasy-timestamp: 1767225600
x-breatheasy-signature: sha256=6f1c...
{
"id": "b1f7...",
"type": "fax.received",
"organization_id": "9c2a...",
"created_at": "2026-01-01T00:00:00.000Z",
"data": {
"fax_id": "77a3...",
"direction": "inbound",
"from_number": "+15035559999",
"to_number": "+15035550100",
"pages": 4,
"routed_extension": "60",
"occurred_at": "2026-01-01T00:00:00.000Z"
}
}Verify the signature before you trust the body:
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, timestamp, signature, secret) {
const expected = "sha256=" + createHmac("sha256", secret)
.update(timestamp + "." + rawBody)
.digest("hex");
const a = Buffer.from(signature);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}Reply with a 2xx within ten seconds. Anything else is retried up to four times with growing delays, and you can replay any event by hand from the Integrations screen.
5. Embed the workspace
Your backend mints a short-lived token for the signed-in staff member, then your page mounts the iframe. Nobody signs in twice. List the exact web addresses that may frame the workspace in Integrations, otherwise the embed is refused.
// your server
const res = await fetch("https://connect.breatheasy.net/api/public/v1/embed-tokens", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.BEC_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ extension: "60", display_name: "Dana R.", ttl_seconds: 3600 })
});
const { data } = await res.json(); // data.token, data.embed_url<!-- your page -->
<div id="phone"></div>
<script src="https://connect.breatheasy.net/embed.js"></script>
<script>
BreatheasyPhone.mount({ target: "#phone", token: "EMBED_TOKEN_FROM_YOUR_SERVER", height: 620 });
</script>Tokens last from one minute to eight hours, are tied to one account, and can carry an extension so the person sees their own voicemails and faxes first.
6. Build your own screens instead
If you would rather keep staff entirely inside your own product, skip the iframe and draw your own interface on top of the API. A typical build uses four calls: list conversations with threads, open one withthreads/{id}/messages, send withPOST messages, and mark it read withPOST threads/{id}/read. Do the same for voicemails and faxes. Subscribe to the webhooks so new activity pushes into your screens instead of polling, and fall back to a poll every thirty seconds if a delivery is missed.
GET /api/public/v1/threads?limit=50
GET /api/public/v1/threads/{id}/messages
POST /api/public/v1/messages {"from":"+1503...","to":"+1503...","body":"..."}
POST /api/public/v1/threads/{id}/read
GET /api/public/v1/voicemails
GET /api/public/v1/voicemails/{id}/audio
POST /api/public/v1/voicemails/{id}/read
GET /api/public/v1/faxes
GET /api/public/v1/faxes/{id}/documentKeep the API key on your server. Calling these endpoints from a browser would expose the key and every account record behind it.
Security notes
- Keys are stored hashed. If a key leaks, revoke it in Integrations and create a new one.
- Every request resolves the account from the key, so ids from another account return nothing.
- Fax PDFs are never public; links expire after five minutes.
- Webhook endpoints must use HTTPS.