Skip to main content

Zanda Public API release notes

Everything we ship, newest first. Each entry names the endpoints it affects, and a breaking change tells you exactly what to update.

Subscribe via RSS or Atom

8 October 2026

Client profiles: Alberta and British Columbia time zones​

A client profile saved with timeZone America/Edmonton, America/Yellowknife, America/Inuvik or Canada/Mountain now reads back as America/Edmonton instead of America/Denver, and one saved with America/Vancouver or Canada/Pacific reads back as America/Vancouver instead of America/Los_Angeles. Time zone answers from GET /api/v1/forms/{id} gain "Calgary, Edmonton" and "Vancouver, Victoria", and the US options now read "Denver, Salt Lake City" and "Los Angeles, Seattle".

30 September 2026

Appointment resource assignment​

PATCH /api/v1/appointments/{id}, PATCH /api/v1/group-appointments/{id} and PATCH /api/v1/group-appointments/{appointmentId}/participants/{clientId} no longer report success when a resource item you asked for could not be attached; the request fails instead of returning 200 with the resource silently missing. Moving an appointment and assigning a resource item in the same request now also attaches it when that resource is only busy at the appointment's previous time.

28 September 2026

Appointment write endpoints are published​

Creating and updating appointments, group appointments and group participants is now available to every practice, and the group appointment operations appear in the API reference for the first time.

23 September 2026

Created and modified dates on payments​

Payment responses now carry created, when the payment was created, and userModified, when its details or invoice allocations were last changed. userModified is null until the payment is first changed. Prefer it over lastModified for detecting edits: lastModified is broader and also moves when the record is rewritten without any of those values changing.

Invoice allocations on payments​

Payment responses now carry an invoices collection: one entry per invoice the payment was allocated to, with a link to it and the amount allocated. An unallocated payment returns an empty collection.

21 September 2026

Write operations are now linked from the responses you already fetch​

Responses using application/vnd.zandaapi.hateoas+json now advertise the write operations available, so you can act by following a link rather than building the URL yourself. A collection carries create; the operations that act on a record — update, delete and the like — are on that record's own response, so fetch it and follow the links it returns.

Personal appointments​

Added GET /api/v1/personal-appointments and GET /api/v1/personal-appointments/{id}, which list the practice's personal appointments and fetch one by id.

Practitioner roster overrides​

Added GET /api/v1/practitioner-roster-overrides and GET /api/v1/practitioner-roster-overrides/{id}, which return the one-off changes made to a practitioner's recurring roster: a day off, a conference, an extra evening clinic. Each override gives the date range and the daily time window it covers, whether it adds or removes availability, and whether those hours can be booked online. Filter by practitioner, location, active status, availability, online bookings, or the date an override is in effect.

17 September 2026

Beta endpoints are labelled​

Endpoints that are still in beta now show a [BETA] prefix or a BETA chip in the documentation pages.

Practitioner rosters​

Added GET /api/v1/practitioner-roster and GET /api/v1/practitioner-roster/{id}, which return a practitioner's recurring working pattern: the date range, the location, how often the pattern repeats, and the time slots for all seven weekdays, including any services a slot excludes from online booking. Filter by practitioner, location, active status, availability, online bookings, or the date a roster is in effect.

15 September 2026

Invoices can be created and updated by every practice​

POST /api/v1/invoices and PATCH /api/v1/invoices/{id} are out of limited release and enabled for all practices. Request and response shapes are unchanged, so an integration built against them during the limited release needs no update.

10 September 2026

Appointments can be invoiced for billable items​

POST /api/v1/appointments takes billableItemIds, the items the appointment is invoiced for, as returned by GET /api/v1/billable-items. Order matters: the first identifier becomes the appointment's service. Send none and the appointment is not invoiced.

Appointments: the invoiced billable items can be replaced​

PATCH /api/v1/appointments/{id} takes billableItemIds as the full set the appointment is invoiced for, replacing whatever is there. Omit it to leave the invoice untouched, or send an empty array to clear the billable items, which leaves the invoice with a zero total rather than deleting it. Anything else on the invoice, such as a session-pack session or a card surcharge, stays as it is.

8 September 2026

Paging by page has been removed​

The page parameter no longer exists on any list endpoint. Every list is paged with cursor. This is a breaking change to any request that still sends page.

Read cursor from the response and send it back to fetch the next page; omit it to start from the beginning. pageSize is unchanged and still sets how many records a page holds. Paging by cursor is forward-only, so there is no page number to jump to and no total to divide.

Before:

GET /api/v1/invoices?page=3&pageSize=25

After:

GET /api/v1/invoices?cursor=CURSOR_FROM_PREVIOUS_RESPONSE&pageSize=25

7 September 2026

Group appointments: add a participant​

POST /api/v1/group-appointments/{appointmentId}/participants adds a client to an existing group booking.

3 September 2026

Group appointments can be created​

POST /api/v1/group-appointments creates a group appointment.

27 August 2026

Group appointments: remove a participant​

DELETE /api/v1/group-appointments/{appointmentId}/participants/{clientId} removes one participant from a group booking, leaving the other participants untouched.

26 August 2026

Resource items: locationId is replaced by a locations collection​

On GET /api/v1/resource-items and GET /api/v1/resource-items/{id}, the single locationId field is gone. A resource item now returns locations, a list of the locations it is available at, each with an id, a name and a link. This is a breaking change to the resource item response.

A resource can be shared across locations, which the single field could not express. Read resourceItem.locations and match on the entry you need instead of comparing locationId. The locationId filter on the list endpoint is unchanged and now matches an item at any of its locations.

Before:

"locationId": 3

After:

"locations": [
{
"id": 3,
"name": "Coburg",
"links": [{ "href": "/api/v1/locations/3", "rel": "self", "method": "GET" }]
},
{
"id": 7,
"name": "Fitzroy",
"links": [{ "href": "/api/v1/locations/7", "rel": "self", "method": "GET" }]
}
]

25 August 2026

Appointments: when scheduleReminder may be set​

On PATCH /api/v1/appointments/{id} and PATCH /api/v1/group-appointments/{appointmentId}/participants/{clientId}, scheduleReminder is rejected once the appointment's start time has passed, and while the attendance state is anything other than Pending or Confirmed unless a reminder has already been sent. Setting it to true also requires a client who can receive reminders. Omit the property in those cases. The rules now match the ones the Zanda app applies.

Invoices can be created against a deactivated practitioner​

POST /api/v1/invoices no longer rejects an invoice whose practitioner has since been deactivated, which matches what the practice can do in the app.

24 August 2026

Invoices: cursor paging could still time out​

Paging through GET /api/v1/invoices with cursor could still fail rather than return a page for some practices; this is now fixed.

20 August 2026

Client profiles can be created and updated by every practice​

POST /api/v1/client-profiles/clients and PATCH /api/v1/client-profiles/clients/{id} are out of limited release and enabled for all practices. Request and response shapes are unchanged, so an integration built against them during the limited release needs no update.

Creating or updating a profile with a lookup id that is no longer active, such as an archived classification, category or status, is rejected with 400. Read the current options from the matching reference-data endpoint.

19 August 2026

Referrals: referrerType is now an object​

On GET /api/v1/referrals and GET /api/v1/referrals/{id}, the referrerType field has changed from a plain text name to an object with an id, a name, and a link to the matching referrer type. This is a breaking change to the referral response.

If you currently read the referrer type's name, change referral.referrerType to referral.referrerType.name. You can now also use referral.referrerType.id to fetch it from GET /api/v1/referrer-types/{id}, or match it against the GET /api/v1/referrer-types list. As before, referrerType is null when a referrer has no type set.

Before:

"referrerType": "General Practitioner"

After:

"referrerType": {
"id": 42,
"name": "General Practitioner",
"links": [{ "href": "/api/v1/referrer-types/42", "rel": "self", "method": "GET" }]
}

Note and form templates​

  • GET /api/v1/note-templates lists the practice's note templates.
  • GET /api/v1/form-templates and GET /api/v1/form-templates/{id} list and fetch form templates.

18 August 2026

Group appointments: edit one participant​

PATCH /api/v1/group-appointments/{appointmentId}/participants/{clientId} updates a single participant's appointment within a group booking, leaving the other participants untouched.

12 August 2026

Group appointments can be updated​

PATCH /api/v1/group-appointments/{id} edits a group appointment.

10 August 2026

Notes​

GET /api/v1/notes and GET /api/v1/notes/{id} return client notes. The list returns note metadata; fetch a single note to read its answers.

Payments can be created, updated and deleted by every practice​

POST /api/v1/payments, PATCH /api/v1/payments/{id} and DELETE /api/v1/payments/{id} are out of limited release and enabled for all practices. Nothing about them changed on the way out, so an integration built against them during the limited release needs no update.

6 August 2026

Appointments: an appointment with no client returned 500​

A client appointment with no client attached made GET /api/v1/appointments fail rather than return the page. Those appointments are now returned normally.

5 August 2026

Each client in a group appointment now carries a link to its own participant appointment, so you can navigate from the group to an individual booking without building the URL yourself.

4 August 2026

Appointments: a resource is validated against the location​

On POST /api/v1/appointments, a resource that does not belong to the appointment's location is now rejected with 400, instead of being accepted and producing an appointment that cannot be opened.

31 July 2026

Invoices: PATCH takes insurer ids, not client-insurer ids​

On PATCH /api/v1/invoices/{id}, the request field clientInsurerIds is renamed to insurerIds, and the values it accepts have changed: pass insurer ids, not the ids of the client-insurer link records. This is a breaking change to the invoice update request.

Take the ids from the insurer resource rather than from the client's insurer list. A request still sending clientInsurerIds is rejected.

30 July 2026

Appointments can be created​

POST /api/v1/appointments creates a single-client appointment.

28 July 2026

Client profiles: the first page could time out​

GET /api/v1/client-profiles could take long enough to fail with a server error rather than return its first page, on practices with many clients.

27 July 2026

Resource items​

GET /api/v1/resource-items and GET /api/v1/resource-items/{id} list and fetch the practice's bookable resources.

Invoices: paging by cursor was slow past the first page​

Following the cursor on GET /api/v1/invoices could take long enough to time out on a large practice.

22 July 2026

Forms​

GET /api/v1/forms and GET /api/v1/forms/{id} list and fetch completed client forms.

Appointments can be updated​

PATCH /api/v1/appointments/{id} edits an existing appointment.

20 July 2026

Group appointments: fetch one participant​

GET /api/v1/group-appointments/{appointmentId}/participants/{clientId} returns a single participant's appointment within a group booking.

16 July 2026

All write endpoints: malformed request bodies are rejected​

Request bodies used to be accepted silently when they carried fields the API did not recognise, or the same field twice. Both now fail with 400 Bad Request and a code of InvalidRequestBody. This is a breaking change to every POST, PUT and PATCH endpoint.

An unknown field, clientIdd for clientId, used to be ignored, so the value you thought you were setting was quietly dropped. A body containing the same property twice used to keep the last one, with no way to tell which had been applied. Both are now errors.

Field names are compared case-insensitively for the duplicate check, so clientId and ClientID in one body is also rejected. Review any payload built by string concatenation, or merged from more than one source.

15 July 2026

Invoices: the embedded referral linked to the wrong record​

The referral embedded on an invoice carried the referrer's id and self link rather than the referral's own, so following the link resolved to a different resource.

14 July 2026

Invoices: an insurer cannot be added twice​

Adding the same insurer to an invoice more than once is now rejected with 400 instead of creating a second identical entry.

Invoices can be created​

POST /api/v1/invoices creates an invoice.

Saleable categories, relationship types and referrer types​

Three more reference lists, each with a list and a by-id endpoint: GET /api/v1/saleable-categories, GET /api/v1/relationship-types and GET /api/v1/referrer-types.

13 July 2026

Appointments: cancellationReason is now an object​

On GET /api/v1/appointments and GET /api/v1/appointments/{id}, cancellationReason has changed from a plain text name to an object with an id, a name and a link to the matching reason. This is a breaking change to the appointment response.

If you read the reason's text, change appointment.cancellationReason to appointment.cancellationReason.name. You can now also match it against GET /api/v1/cancellation-reasons. As before, it is null when the appointment was not cancelled.

Before:

"cancellationReason": "Client unwell"

After:

"cancellationReason": {
"id": 7,
"name": "Client unwell",
"links": [{ "href": "/api/v1/cancellation-reasons/7", "rel": "self", "method": "GET" }]
}

Cancellation reasons and appointment flags​

GET /api/v1/cancellation-reasons and GET /api/v1/appointment-flags list the practice's cancellation reasons and appointment flags, each with a by-id endpoint.

7 July 2026

Paging by page is deprecated​

The sortable list endpoints now return Deprecation, Sunset and Link headers when you page with page. It keeps working until the sunset date the header carries, and the Link header points at the pagination documentation. Move to cursor before then.

Cursor pagination on the sortable list endpoints​

Pass the cursor value returned by the previous response to fetch the next page; omit it to start from the beginning. Paging this way is forward-only, so there is no previous-page link, and it stays fast on a large practice however deep you go.

25 June 2026

Pronouns​

GET /api/v1/pronouns and GET /api/v1/pronouns/{id} list and fetch the practice's pronoun options, and a client profile now embeds the client's pronouns as an object with an id, a name and a link.

24 June 2026

Client classifications​

GET /api/v1/client-classifications and GET /api/v1/client-classifications/{id} list and fetch the practice's client classifications, and a client profile now embeds clientClassifications, a list of the classifications assigned to the client, each with an id, a name and a link.

22 June 2026

Client profiles: country and timeZone are validated​

On client profile create and update, values outside the supported country list, and time zones that are not valid IANA identifiers, are now rejected with 400 instead of being stored. Existing profiles are unaffected.

15 June 2026

Referrals: startDate and endDate are dates, not date-times​

On GET /api/v1/referrals and GET /api/v1/referrals/{id}, the two referral dates are returned as plain dates rather than as timestamps at midnight. The matching filters, startDateFrom, startDateTo, endDateFrom and endDateTo, accept a plain date too. This is a breaking change if you parse either field as a date-time.

A referral begins and ends on a day, not at an instant, so the time component was always zero and the timezone it implied was misleading. Parse both fields as dates, and send dates when you filter. Both remain null when the referral has no such date set.

Before:

"startDate": "2026-06-15T00:00:00Z"

After:

"startDate": "2026-06-15"

Invoices: deep pages timed out​

Requesting a high page number from GET /api/v1/invoices could take long enough to fail with a server error rather than return.

11 June 2026

Client profiles carry their custom profile fields​

A client profile response now lists the values recorded against the practice's custom profile fields, each linked to its definition in GET /api/v1/custom-profile-fields.

Client profiles: the marketing source is an embedded object​

marketingSource now carries the source's id and name and a link to GET /api/v1/marketing-sources/{id}, instead of a name on its own.

8 June 2026

Custom profile fields​

GET /api/v1/custom-profile-fields and GET /api/v1/custom-profile-fields/{id} list the custom fields a practice has defined for its client profiles, and fetch one by id.

Marketing sources​

GET /api/v1/marketing-sources and GET /api/v1/marketing-sources/{id} list the practice's marketing sources, and fetch one by id.

4 June 2026

Rate limit headers on successful responses​

Every successful response now carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, so you can pace a batch of requests instead of discovering the limit by being refused.

1 June 2026

Page size now ranges from 1 to 100​

pageSize accepts anything from 1 to 100, where it previously accepted 5 to 25. The default is unchanged at 10 records.

Referrals: client and referrer details were swapped​

GET /api/v1/referrals returned the client's details in the referrer fields and the referrer's in the client fields.

28 May 2026

Client profiles carry their active insurers​

GET /api/v1/client-profiles/{id} now returns the client's active insurers, so you no longer need a second call to work out who covers them.

27 May 2026

An entry in an invoice's invoicePayments now includes the amount applied to that invoice, so a payment split across several invoices no longer has to be reconciled by hand.

19 May 2026

Invoices can be updated​

PATCH /api/v1/invoices/{id} updates an existing invoice.

11 May 2026

Paged responses: totalItemCount and pageCount are gone, and pageNumber is now page​

The envelope every list endpoint returns has changed. pageNumber is renamed to page, and the two totals, totalItemCount and pageCount, have been removed. This is a breaking change to every list endpoint.

Counting the whole result set on every request is what made large lists slow, and no total can be returned without doing it. To walk a list, read hasNextPage and request the next page while it is true, rather than dividing a total by your page size. If you display a count, you will need to stop showing one, or count what you have fetched. items, links and pageSize are unchanged.

Before:

{
"pageNumber": 1,
"pageSize": 10,
"totalItemCount": 438,
"pageCount": 44,
"hasNextPage": true
}

After:

{
"page": 1,
"pageSize": 10,
"hasNextPage": true
}

4 May 2026

Sorting on client profiles​

You can now sort GET /api/v1/client-profiles by lastName, name, dateAdded, clientNumber or id. Every list endpoint now sorts.

28 April 2026

Every record reports when it last changed​

Each of the nine resources now returns a lastModified timestamp, and every list endpoint can be filtered by modifiedSince. Together they let you poll for what has changed since your last call instead of reading everything each time.

21 April 2026

Client profiles carry their profile roles​

A client profile now reports the roles it holds in the practice, and you can filter GET /api/v1/client-profiles by profileRoles to list only the profiles holding a given role.

20 April 2026

Filter by date in the practice's time zone​

Send an X-Time-Zone header with an IANA time zone name, such as Australia/Sydney, and date filters are interpreted in that zone rather than in UTC. An unknown name is rejected. Without the header, behaviour is unchanged.

Sex, gender and gender identity​

GET /api/v1/sexes, GET /api/v1/genders and GET /api/v1/gender-identities list the values a practice can record, each with a by-id endpoint.

Client profiles carry the client's identity​

A client profile response now includes the client's identity details, drawn from the same values those three endpoints list.

Sorting by date sorted incorrectly​

A sort on a date field ordered records by the text of the date rather than by the date itself, so pages came back in the wrong order.

7 April 2026

Payments: paidBy was always null​

Every payment returned paidBy as null, whoever had made the payment. It now names them.

6 April 2026

Sorting on payments​

You can now sort GET /api/v1/payments by dateReceived, total or id.

Sorting on referrals​

You can now sort GET /api/v1/referrals by name, dateFrom, dateTo or id.

31 March 2026

Client profiles: the custom category is an embedded object​

customCategory now carries the category's id and name and a link to it, the same shape customStatus was given earlier this month.

Sorting on appointments​

You can now sort GET /api/v1/appointments by dateCreated, dateFrom, dateTo, id or flag, with the sort parameter. Add :desc to reverse a field, and separate several with commas, as in sort=dateFrom:desc,id.

Sorting on invoices​

You can now sort GET /api/v1/invoices by invoiceDate, invoiceDueDate, totalCharges or id.

Payments carry their location​

A payment response now names the location it was taken at.

25 March 2026

Filtering on referrals​

You can now filter GET /api/v1/referrals by isActive, clientId, startDateFrom, startDateTo, endDateFrom and endDateTo.

Appointments and payments can be filtered by isActive​

Pass isActive=true to leave out records the practice has since deactivated, or isActive=false to see only those. The other list endpoints already had it.

19 March 2026

Payments: the payment method is an embedded object​

method on a payment now carries the method's id and name and a link to GET /api/v1/payment-methods/{id}, instead of a name on its own.

Client profiles: the custom status is an embedded object​

customStatus on a client profile now carries the status's id and name and a link to it. It is null when the profile has no status set.

18 March 2026

Payment methods​

GET /api/v1/payment-methods and GET /api/v1/payment-methods/{id} list the practice's payment methods and fetch one by id.

17 March 2026

Filtering on invoices​

You can now filter GET /api/v1/invoices by isActive, isPaid, clientId, practitionerId, locationId, invoiceDateFrom, invoiceDateTo, dueDateFrom and dueDateTo.

12 March 2026

Filtering on appointments​

You can now filter GET /api/v1/appointments by dateFrom, dateTo, clientId, practitionerId and locationId.

10 March 2026

Filtering on client profiles​

You can now filter GET /api/v1/client-profiles by isActive, isArchived, dateAddedFrom, dateAddedTo, primaryPractitionerId, customStatusId and customCategoryId.

Filtering on practitioners​

You can now filter GET /api/v1/practitioners by isActive, profession, jobTitle and emailAddress.

Filtering on payments​

You can now filter GET /api/v1/payments by clientId, clientNumber, methodId, receivedAfter, receivedBefore, minAmount and maxAmount.

Paging returned records more than once​

Records could appear on two pages, or on none, because list results had no stable order when two records shared a sort value. Every list endpoint now orders deterministically.

9 March 2026

Billable items: pricing is now correct​

GET /api/v1/billable-items could return a price that did not match the one the practice had set.

18 February 2026

The Zanda Public API is available​

The API gives you read access to a practice's scheduling and billing data, versioned under /api/v1. Nine resources are available, each as a list and as a single record: appointments, billable-items, client-profiles, insurers, invoices, locations, payments, practitioners and referrals.

Authenticate with the practice's API key in an X-API-KEY header. Lists are paged with page and pageSize, which returns 10 records by default and 25 at most, and every response carries links to its related records so you do not have to build the next URL yourself. Requests are rate limited.

Reference documentation for every endpoint is published with the API.