DATEYE

Matching & Sync

Pending imports & linking (human, in the portal)

There is no automatic matching in the API — lacking PII, the server cannot map pseudonyms to people. This happens in the portal:

  1. First uploads (first_visit) accumulate as pending entries, grouped by devicePseudonym.
  2. A staff member links a pseudonym to a real patient record in the portal. This creates the mapping devicePseudonym → clientId (the device-pseudonym table) and a persistentToken for that patient.
  3. From then on the mapping sync (see below) delivers mkClientId for the pseudonym to the client, and the client subsequently sends as follow_up/token — directly attributed.

GET /api/v1/imports/list — pending overview (for your portal/debugging)

Query: ?status=pending|rejected|all (default pending), optional &countOnly=1.

Full list:

{
  "success": true,
  "data": {
    "pseudonyms": [
      { "devicePseudonym": "a1b2…", "initialsHint": "AB", "dobMonthYearHint": "1985-03",
        "status": "pending", "measurementCount": 5, "newestMeasuredAt": "2026-07-15T10:30:00.000Z" }
    ],
    "count": 50,
    "total": 120
  }
}

Counts only (countOnly=1):

{ "success": true, "data": {
  "pendingPseudonyms": 12, "pendingMeasurements": 48,
  "rejectedPseudonyms": 3, "rejectedMeasurements": 7 } }

Mapping sync — GET /api/v1/practice/mappings?since=<ISO-8601>

Authenticated. The client polls incrementally (typically 1×/h) and caches locally to know which pseudonyms are linked (→ follow_up) or revoked.

since is required (ISO-8601; for the first sync e.g. 1970-01-01T00:00:00.000Z). Missing/invalid → 400 VALIDATION_ERROR.

Response 200 (Cache-Control: no-store):

{
  "success": true,
  "data": {
    "mappings": [
      { "devicePseudonym": "a1b2…", "mkClientId": "<uuid>", "revoked": false },
      { "devicePseudonym": "f5e6…", "mkClientId": null, "revoked": true, "revokedAt": "2026-07-14T09:00:00.000Z" }
    ],
    "nextSince": "2026-07-15T10:30:00.001Z"
  }
}
  • Active link: mkClientId set, revoked: false.
  • Tombstone: revoked: true (+ revokedAt, mkClientId: null) ⇒ invalidate the mapping locally, stop uploading for this pseudonym. Triggers: manual reset of the link, deletion of the patient, or consent withdrawal.
  • Use nextSince as since on the next poll (cursor). The cursor is deterministic (secondary sort on the pseudonym) so equal timestamps cannot cause an infinite loop.

Rejected pseudonyms — GET /api/v1/rejected-clients

Authenticated. Returns the full, deduplicated list of all currently rejected pseudonyms of the practice. The client caches it and skips these pseudonyms on the next export.

Response 200:

{ "rejected": [ { "pseudonym": "a1b2…" }, { "pseudonym": "f5e6…" } ] }

An empty list = nothing rejected (no 404). Content is identical to skip_pseudonyms[] from the import response — both are the same source.

Rejection lifecycle (portal-side): A staff member rejects a pseudonym → its existing pending entries become rejected, and newly arriving measurements for that pseudonym are automatically buffered as rejected (not pending). Reactivating resets them to pending.