iClinic Developers

Incremental sync

In the iClinic Integration API, patients, appointments, receipts and therapy plans share one way to read changes: you fetch the records that changed since a point in time, one page at a time, using a cursor. For how cursor paging works, see Pagination.

Query parameters

ParameterDescription
updatedSincestring · date-timeoptional Returns only records whose updatedAt is at or after this time, including a record at exactly this time. Use a full ISO 8601 timestamp with a time zone, for example 2026-10-01T00:00:00.000Z (write the + of a time zone offset as %2B). If you leave it out, the API returns all records.
cursorstringoptional The exact nextCursor value from the previous page. Do not construct or parse it yourself. Use it only with the path that returned it.
limitintegeroptional From 1 to 500. Default 200.

The response has the form { requestId, <resources>, nextCursor } (for example patients). nextCursor is null on the last page. A parameter that is not in the table is rejected with 400 UNKNOWN_PARAMETER, so a typo in updatedSince cannot silently turn into a full reload.

How to sync

  1. First run: call without updatedSince and follow nextCursor until it is null.
  2. For each record: if it has deletedAt, delete it on your side by id. Otherwise overwrite by id (do not insert a duplicate).
  3. Keep track of the largest updatedAt you have seen, and store it only after you have read all the pages.
  4. Later runs: set updatedSince to the stored value minus 5 minutes, and keep it the same on every page of that run.
Why go back 5 minutes. The API delivers records at least once: a record can appear again, and a record that was just written can carry an earlier time than a record you have already read. A small overlap, combined with overwriting by id, makes both cases harmless.

Deleted records (tombstones)

A deleted or merged record stays in the list in a short form, { id, deletedAt, updatedAt }, with no personal data (a patient record also has mergedIntoId). A GET …/{id} request for such a record still returns 200 with the tombstone. A request for an id that does not exist, or that belongs to another clinic, returns the same 404 NOT_FOUND in both cases.

curl -sS "https://iclinic.phongkhamhieploi.vn/api/integration/v1/patients?limit=2&updatedSince=2026-10-01T00:00:00.000Z" \
  -H "Authorization: Bearer $ICLINIC_API_KEY"
# → 2 records, "nextCursor": "djF8cGF0aWVudHN8…"

curl -sS "https://iclinic.phongkhamhieploi.vn/api/integration/v1/patients?limit=2&updatedSince=2026-10-01T00:00:00.000Z&cursor=djF8cGF0aWVudHN8…" \
  -H "Authorization: Bearer $ICLINIC_API_KEY"
# → 1 record, "nextCursor": null  (end of the list)