Versioning and Diff
How It Works
The Nompd API uses a version-based diff mechanism that allows you to fetch only the data that has changed since your last request. This is useful for keeping a local cache in sync without downloading the full dataset each time.
Opting in to the versioned envelope
By default,
treatment-groupandreimbursement-groupresponses return the data directly (anentriesarray of plain items for list endpoints, or a single plain object for/{id}) — withoutcurrentVersion,version,lastChanged, orisDeleted. This default applies wheneversince-versionis omitted.To use the version-based diff workflow described below, add
versioned=trueto your request (e.g.?versioned=trueor?since-version=5&versioned=true). Requests that includesince-versionalways return the versioned envelope, regardless of theversionedparameter, since a diff response is meaningless without version metadata.
Version Number
Every response includes a currentVersion number. This is a monotonically increasing integer that increments each time the server syncs with upstream data sources (typically once per day).
Workflow
Client Server
│ │
│ GET /api/v1/treatment-group │
│──────────────────────────────────>│
│ │
│ { currentVersion: 5, entries: [...all items...] }
│<──────────────────────────────────│
│ │
│ (store version = 5 locally) │
│ │
│ ... time passes, nightly sync happens ...
│ │
│ GET /api/v1/treatment-group?since-version=5
│──────────────────────────────────>│
│ │
│ { currentVersion: 6, entries: [...only changes...] }
│<──────────────────────────────────│
│ │
│ (update version = 6 locally) │
Step by Step
1. Initial fetch
Fetch all data without since-version, opting in to the versioned envelope with versioned=true:
curl -H "X-API-KEY: key" \
"https://api.example.com/api/v1/treatment-group?versioned=true"
Response:
{
"currentVersion": 5,
"entries": [
{ "treatmentGroup": { "id": "aaa", "name": "Group A" }, "isDeleted": false },
{ "treatmentGroup": { "id": "bbb", "name": "Group B" }, "isDeleted": false }
]
}
Store currentVersion: 5 in your system.
2. Subsequent fetches
Pass the stored version as since-version:
curl -H "X-API-KEY: key" \
https://api.example.com/api/v1/treatment-group?since-version=5
Response (only changes):
{
"currentVersion": 6,
"entries": [
{ "treatmentGroup": { "id": "aaa", "name": "Group A (updated)" }, "isDeleted": false },
{ "treatmentGroup": { "id": "ccc", "name": "Group C" }, "isDeleted": false },
{ "treatmentGroup": { "id": "bbb", "name": "Group B" }, "isDeleted": true }
]
}
Update your stored version to 6.
3. Apply changes locally
For each item in the diff response:
isDeleted: false— Upsert (insert or update) the item in your local store.isDeleted: true— Remove the item from your local store.
An isDeleted: true entry can represent either a deletion from the upstream source or a
response-only FEST visibility transition: when an active group loses its final FEST-valid
product, the default full response no longer contains that group, so the diff emits a synthetic
tombstone to remove any previously cached copy. The database row remains active, and a later FEST
match can reactivate the group through a subsequent isDeleted: false diff entry.
4. Handle empty diffs
If nothing changed, the response will have an empty entries array:
{
"currentVersion": 6,
"entries": []
}
Checking for changes before downloading
If you download the full dataset instead of using since-version, use
GET /api/v1&v2/last-updated first. It returns no group
data, only whether treatment groups and/or reimbursement groups changed on or after a UTC date.
- Poll
last-updated?since=<savedDate>. On the first run, omitsince. - Download both resource types on the first run, or when
hasChangesistrue. Before starting the downloads, note the current UTC date of the poll (use the response'sDateheader rather than your own clock). - Save that date as
savedDateonly after all downloads succeed. If a download fails, keep the old date and retry.
curl -si -H "X-API-KEY: key" \
"https://api.example.com/api/v1/last-updated?since=2026-10-06"
Why the date of the poll, and not of lastUpdated:
- Changes made after your poll happen on that date or later, so the next
since=<savedDate>cannot miss them, even if they happen while you download or after midnight. since=<date of lastUpdated>would staytrueforever, because the change itself is on or after midnight of its own date.
Limits of a date-only cursor:
- A change earlier on the same UTC day as your poll is already in your download, but is reported
again by the next poll, so expect one redundant download after a change. Once a poll date has no
changes,
hasChangesisfalse. - The comparison is against UTC midnight, not your local time zone.
- The check assumes the server's change times do not move backwards. Reload everything and reset your saved date after a restore or when switching environments.
- This endpoint is independent of
currentVersion, which also increases on syncs that change nothing. Do not mix the two cursors.
Client Implementation Example
public class NompdClient
{
private long _lastVersion = 0;
private readonly Dictionary<string, TreatmentGroup> _cache = new();
public async Task SyncAsync()
{
var url = _lastVersion == 0
? "/api/v1/treatment-group"
: $"/api/v1/treatment-group?since-version={_lastVersion}";
var response = await _httpClient.GetFromJsonAsync<VersionedResponse>(url);
foreach (var item in response.Items)
{
if (item.IsDeleted)
_cache.Remove(item.Data.Id);
else
_cache[item.Data.Id] = item.Data;
}
_lastVersion = response.CurrentVersion;
}
}
Edge Cases
| Scenario | Behavior |
|---|---|
since-version=0 |
Returns all items (equivalent to full fetch, but includes deleted items) |
since-version higher than current |
Returns empty entries list with current version |
| Multiple syncs between your requests | All accumulated changes are included |
| Client has never fetched before | Omit since-version to get all active items |
| Group loses its final FEST-valid product | Returns isDeleted: true so clients remove it |
| A FEST-hidden group gains a valid product | Returns isDeleted: false so clients upsert it again |
Recommendations
- Always store
currentVersionfrom every response, even if the entries list is empty. - Use full fetch for initial load (no
since-version). This returns only active items, giving you a clean starting point. - Use
since-versionfor subsequent calls. This is more efficient and includes deletion information. - Sync at most once per day. Data updates nightly, so more frequent polling gives no benefit.