Get started

Requests, responses & errors

The few rules every endpoint follows. Learn them once and the whole API feels the same.

Base URL#

Every endpoint starts with this address (it is the same site as HealthFix itself):

Base URL
https://your-healthfix-site/api/v1

So “GET /patients” in these docs means a GET request to https://your-healthfix-site/api/v1/patients. Always use https.

Headers to send#

HeaderWhenValue
AuthorizationAlwaysBearer YOUR_API_KEY
Content-TypeWhen you send a body (POST, PUT, PATCH)application/json

Methods#

MethodMeansExample
GETRead somethingGET /appointments
POSTCreate something newPOST /patients
PUTReplace something with a full new versionPUT /appointments/{id}
PATCHChange one partPATCH /appointments/{id}/status

In paths, {id} is a placeholder for the record's ID, a 36-character code like 0b9f6f7e-3c1a-4f7e-9b1e-5f2d8a7c1e42 that you get from earlier responses.

Successful responses#

Successful answers use status 200 (or 201 when something was created) and always put the result in data:

json
{ "data": { "id": "…", "full_name": "Ravi Kumar", "…": "…" } }

Fields that have no value are null. Unknown fields you send are rejected, which catches typos early.

Dates, times and money#

  • Dates are YYYY-MM-DD, e.g. 2026-10-01.
  • Times are ISO 8601 with a time zone, e.g. 2026-10-01T10:30:00+05:30 (10:30 in India) or 2026-10-01T05:00:00Z (UTC). HealthFix answers in UTC (the Z).
  • The clinic's own time zone and currency are in GET /clinic/preferences.
  • Amounts are plain numbers in the clinic's currency, e.g. 500 means ₹500.

Long lists (pagination)#

Lists come in pages. Use limit for the page size; when there are more, meta tells you how to get the next page:

json
{
  "data": [ … 25 patients … ],
  "meta": { "has_more": true, "next_cursor": "MjAyNi0wMy0wMlQxMDoxNTowMFp8…" }
}

Ask again with ?cursor= set to next_cursor, and repeat until has_more is false:

javascript
let cursor = "";
const all = [];
do {
  const res = await fetch(`https://your-healthfix-site/api/v1/patients?limit=100&cursor=${cursor}`, {
    headers: { Authorization: "Bearer " + process.env.HEALTHFIX_API_KEY },
  });
  const body = await res.json();
  all.push(...body.data);
  cursor = body.meta?.has_more ? body.meta.next_cursor : "";
} while (cursor);

Search results (?q=) are different: they return the best matches in one go, without pages.

Errors#

When something goes wrong you get a status of 400 or higher and an error object with a code for your program and a message for people:

json
{
  "error": {
    "code": "validation_failed",
    "message": "One or more fields are invalid",
    "fields": { "gender": "is required" },
    "request_id": "api-7f3c/Qx9bT2kLmN-000042"
  }
}
StatusCodeWhat it means and what to do
400bad_requestSomething in the request is malformed, like an invalid date. Read the message.
401unauthorized / api_key_invalidThe key is missing, mistyped, expired or revoked. Check the Authorization header.
403api_key_scopeThe key works but lacks the scope for this endpoint. Create a key with that scope.
403api_key_forbiddenKeys can't use this endpoint at all (for example deleting). Use the app.
404not_foundNo record with that ID in your clinic.
409conflictClashes with existing data, e.g. the doctor already has an appointment at that time.
422validation_failedSome fields are wrong. fields says which, in plain English.
429rate_limitedToo many requests. Wait a second and retry.
402 / 403subscription codesThe clinic's plan has lapsed or is read-only. Reading still works; writing is paused.
500internal_errorOur fault. Retry later; if it keeps happening, send us the request_id.
Always log error.request_id. With it, support can find exactly what happened to that call.

Rate limits and retries#

Up to 5 requests a second per key, bursts to 60. If you get 429, or a 5xx error, wait and retry, doubling the wait each time (1 s, 2 s, 4 s…). Don't automatically retry 4xx errors other than 429; they won't fix themselves.

A small retry helper
async function healthfix(path, options = {}, attempt = 0) {
  const res = await fetch("https://your-healthfix-site/api/v1" + path, {
    ...options,
    headers: {
      Authorization: "Bearer " + process.env.HEALTHFIX_API_KEY,
      "Content-Type": "application/json",
      ...options.headers,
    },
  });
  if ((res.status === 429 || res.status >= 500) && attempt < 4) {
    await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
    return healthfix(path, options, attempt + 1);
  }
  const body = await res.json();
  if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
  return body.data;
}

// Usage
const doctors = await healthfix("/users?role=doctor");