Connect
Step-by-step recipes
Complete, copy-and-paste walkthroughs for the most common integrations. Each one lists the scopes its key needs.
- Take bookings on your own website
- Today's appointments in Google Sheets
- Zapier, Make or n8n (no code)
- “When is my appointment?” bot
- Daily collections email or accounting sync
Take bookings on your own website#
Want zero code?
https://your-healthfix-site/book/your-clinic. Turn it on under Settings → Clinic → Online booking and link to it from your site. Use the API below only if you want the form inside your own design.Key scopes: patients:read, patients:write, appointments:read, appointments:write.
Keep the key on your server
Your website's form sends the visitor's details to your server (or a serverless function), and your server talks to HealthFix. Never call HealthFix from browser JavaScript, because visitors could read the key.
Show the doctors
Call
GET /users?role=doctoronce and cache it. Each doctor has anidyou'll need for booking.GET /schedulesgives their weekly timings and leave, so you only offer real times.Find or register the patient
Search by phone first so returning patients aren't duplicated. If nobody matches, register them.
Book, and handle a taken slot
Send the booking. If someone else took the time a moment earlier you get
409: ask the visitor to pick another time.
import express from "express";
const app = express();
app.use(express.json());
const API = "https://your-healthfix-site/api/v1";
const headers = {
Authorization: "Bearer " + process.env.HEALTHFIX_API_KEY,
"Content-Type": "application/json",
};
async function hf(path, init = {}) {
const res = await fetch(API + path, { ...init, headers });
const body = await res.json();
if (!res.ok) throw Object.assign(new Error(body.error.message), { status: res.status });
return body.data;
}
// The website form posts: { name, phone, gender, doctorId, startsAt }
// startsAt looks like "2026-10-01T10:30:00+05:30"
app.post("/book", async (req, res) => {
const { name, phone, gender, doctorId, startsAt } = req.body;
try {
// 1. Returning patient? Search by phone.
const matches = await hf("/patients?q=" + encodeURIComponent(phone) + "&limit=5");
let patient = matches.find((p) => p.phone.endsWith(phone.slice(-10)));
// 2. New patient: register them.
if (!patient) {
patient = await hf("/patients", {
method: "POST",
body: JSON.stringify({ full_name: name, gender, phone }),
});
}
// 3. Book the appointment.
const appt = await hf("/appointments", {
method: "POST",
body: JSON.stringify({
patient_id: patient.id,
doctor_id: doctorId,
scheduled_at: startsAt,
duration_min: 15,
reason: "Booked on website",
}),
});
res.json({ ok: true, appointmentId: appt.id });
} catch (err) {
if (err.status === 409) return res.status(409).json({ ok: false, message: "That time was just taken. Please pick another." });
res.status(500).json({ ok: false, message: "Booking failed. Please call the clinic." });
}
});
app.listen(3000);Today's appointments in Google Sheets#
Key scope: appointments:read. Refreshes a sheet with the day's appointments every morning, with no server needed.
Open Apps Script
Create a Google Sheet, then click Extensions → Apps Script.
Store the key safely
In Apps Script click the gear icon (Project Settings) → Script properties → Add property. Name:
HEALTHFIX_API_KEY, value: your key. This keeps it out of the code.Paste the script and run it
Replace the code in the editor with the script below, click Save, choose
importTodayand click Run. Google asks for permission the first time; allow it.Run it every morning
Click the clock icon (Triggers) → Add trigger → function
importToday, event source Time-driven, Day timer, 7 to 8 am.
function importToday() {
const key = PropertiesService.getScriptProperties().getProperty("HEALTHFIX_API_KEY");
const tz = "Asia/Kolkata"; // your clinic's time zone
const day = Utilities.formatDate(new Date(), tz, "yyyy-MM-dd");
const from = day + "T00:00:00+05:30";
const to = Utilities.formatDate(new Date(Date.now() + 864e5), tz, "yyyy-MM-dd") + "T00:00:00+05:30";
const url = "https://your-healthfix-site/api/v1/appointments?from=" + encodeURIComponent(from) + "&to=" + encodeURIComponent(to);
const res = UrlFetchApp.fetch(url, { headers: { Authorization: "Bearer " + key }, muteHttpExceptions: true });
const body = JSON.parse(res.getContentText());
if (res.getResponseCode() !== 200) throw new Error(body.error.message);
const rows = body.data.map((a) => [
Utilities.formatDate(new Date(a.scheduled_at), tz, "HH:mm"),
a.patient_name, a.patient_code, a.patient_phone, a.doctor_name, a.status, a.reason || "",
]);
const sheet = SpreadsheetApp.getActiveSheet();
sheet.clear();
sheet.appendRow(["Time", "Patient", "ID", "Phone", "Doctor", "Status", "Reason"]);
if (rows.length) sheet.getRange(2, 1, rows.length, rows[0].length).setValues(rows);
}Zapier, Make or n8n (no code)#
Every no-code tool has a “web request” step. Fill it in like this, whatever the tool:
| Setting | What to enter |
|---|---|
| Step / module | Zapier: Webhooks by Zapier → Custom Request. Make: HTTP → Make a request. n8n: HTTP Request node. |
| Method | GET to read, POST to create (as in the API reference) |
| URL | https://your-healthfix-site/api/v1/appointments |
| Header 1 | Authorization = Bearer YOUR_API_KEY |
| Header 2 (POST only) | Content-Type = application/json |
| Body (POST only) | The JSON from the reference, with fields mapped from earlier steps |
| Parse response | Yes / JSON. The results are under data. |
Authorization, value Bearer …) instead of typing it into the node, so it stays encrypted.Popular ideas: a new Typeform or Google Form answer → register the patient; every evening → billing summary into a Slack message; a new unpaid invoice → reminder task in your CRM.
“When is my appointment?” bot#
Key scopes: patients:read, appointments:read. Works for WhatsApp Business, Telegram or a website chat: the bot receives the sender's phone number and replies.
Find the patient by phone
Search with the last 10 digits of the sender's number. Search ignores +91, spaces and dashes.
Get their appointments and pick the next one
Take appointments that are in the future and not cancelled; the earliest is the answer.
Reply in the clinic's time zone
Times come back in UTC, so format them in the clinic's zone (from
GET /clinic/preferences).
async function nextAppointmentReply(senderPhone) {
const phone = senderPhone.replace(/\D/g, "").slice(-10);
const [patient] = await hf("/patients?q=" + phone + "&limit=1");
if (!patient || !patient.phone.endsWith(phone)) return "We couldn't find your record. Please call the clinic.";
const visits = await hf(`/patients/${patient.id}/appointments`);
const next = visits
.filter((a) => new Date(a.scheduled_at) > new Date() && a.status !== "cancelled")
.sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at))[0];
if (!next) return `Hi ${patient.full_name}, you have no upcoming appointments.`;
const when = new Date(next.scheduled_at).toLocaleString("en-IN", {
timeZone: "Asia/Kolkata", weekday: "long", day: "numeric", month: "short", hour: "numeric", minute: "2-digit",
});
return `Hi ${patient.full_name}, your appointment with ${next.doctor_name} is on ${when}.`;
}
// hf() is the small helper from the website booking recipe above.Daily collections email or accounting sync#
Key scope: billing:read. Run once a day (cron, GitHub Actions, a serverless schedule) after the clinic closes.
import os, datetime, requests
API = "https://your-healthfix-site/api/v1"
HEADERS = {"Authorization": "Bearer " + os.environ["HEALTHFIX_API_KEY"]}
today = datetime.date.today().isoformat()
summary = requests.get(f"{API}/billing/summary", params={"from": today, "to": today},
headers=HEADERS, timeout=30).json()["data"]
lines = [f"Collections for {today}: {summary['collected']:.0f}"]
lines += [f" {m['method']}: {m['amount']:.0f} ({m['count']} payments)" for m in summary["by_method"]]
lines.append(f"Outstanding dues: {summary['outstanding']:.0f} across {summary['outstanding_count']} invoices")
print("\n".join(lines)) # send by email, Slack or push into your accounting toolFor line-by-line accounting, page through GET /invoices?from=…&to=… (see pagination) and fetch each invoice with GET /invoices/{id} for items, tax and payments.