Skip to main content

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 endpoints
  • GET /api/public/v1/calls - call history with outcome, duration, and AI summary
  • GET /api/public/v1/messages - individual text messages
  • GET /api/public/v1/threads - text conversations
  • GET /api/public/v1/threads/{id}/messages - one conversation, oldest first
  • GET /api/public/v1/faxes - sent and received faxes
  • GET /api/public/v1/faxes/{id}/document - five minute download link for the PDF
  • GET /api/public/v1/voicemails - voicemails with transcripts
  • GET /api/public/v1/voicemails/{id}/audio - playback link for the recording
  • GET /api/public/v1/extensions - your extension directory
  • GET /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}/document

Keep 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.