API reference

Members

Members (patients) the partner shares with the clinic, keyed by the partner's own id. 5 operations under /public/v1, with parameters, request bodies and example responses.

Paths are relative to the API host: https://sandbox-api.clivehealth.app for the sandbox, https://api.clivehealth.app for production. Send Authorization: Bearer <token> on every request (Getting started).

Operations

List members

GET/public/v1/members

Scope members:readOperation listMembers

Active and inactive members, ordered by updated_at then id.

Requires scope members:read.

Parameters

listMembers parameters
NameInTypeDescription
limitqueryintegerPage size, 1 to 200. Default 50.
cursorquerystringOpaque cursor from the previous page's next_cursor.
updated_sincequerystring (date-time)ISO 8601 timestamp: only records updated at or after it. Lists are ordered by updated_at then id, so this plus cursor is a change feed.
membership_noquerystringExact membership number.
statusquery"active" | "inactive"

Responses

200A page of members.Returns MemberPage.

listMembers 200 response
FieldTypeDescription
datarequiredarray of Member
idrequiredstring (uuid)Clive member id.
statusrequired"active" | "inactive"
full_namerequiredstring or null
phonerequiredstring or null
emailrequiredstring or null
date_of_birthrequiredstring or nullYYYY-MM-DD.
identity_typerequired"mykad" | "mypr" | "mykas" | "mytentera" | "passport" | "other" | nullWhat identity_number is: MyKad, passport, ...
identity_numberrequiredstring or nullIC or passport number.
membershipsrequiredarray of Membership
providerrequiredstringPartner system the id belongs to, e.g. partner_v1.
external_idrequiredstringThe member's id in that system.
membership_norequiredstring or nullMembership or card number, when the partner has one.
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
next_cursorrequiredstring or nullPass as cursor to fetch the next page; null on the last page.
has_morerequiredboolean
Response 200JSON
{
  "data": [
    {
      "id": "c41f7a52-8d1e-4b2a-9f3c-6e5d4c3b2a10",
      "status": "active",
      "full_name": "Siti Aminah binti Ahmad",
      "phone": "+60123456789",
      "email": "siti@example.com",
      "date_of_birth": "1990-01-01",
      "identity_type": "mykad",
      "identity_number": "900101-14-5678",
      "memberships": [
        {
          "provider": "partner_v1",
          "external_id": "MEM-0012345",
          "membership_no": "PM00012345"
        }
      ],
      "created_at": "2026-10-01T00:00:00.000Z",
      "updated_at": "2026-10-24T02:15:09.123Z"
    }
  ],
  "next_cursor": null,
  "has_more": false
}
listMembers error responses
StatusMeaning
400Malformed request or invalid cursor.
401Missing, invalid or expired access token, or the API client was revoked (INVALID_TOKEN).
403The token lacks the scope this operation needs (INSUFFICIENT_SCOPE).
422Validation failed (VALIDATION_ERROR).
429Per-client rate limit exceeded (RATE_LIMITED).
500Unexpected error (INTERNAL_ERROR).

Every error body is the error envelope; branch on its code.

Create or update a member by external id

POST/public/v1/members

Scope members:writeOperation upsertMember

Keyed on (provider, external_id): creates the member the first time and updates it afterwards. Returns the Clive member id. PII is encrypted at rest.

Requires scope members:write.

Parameters

upsertMember parameters
NameInTypeDescription
Idempotency-KeyrequiredheaderstringUnique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours.

Request body MemberUpsert

upsertMember request body
FieldTypeDescription
providerrequiredstringYour system's provider code, e.g. partner_v1.
external_idrequiredstringThe member's id in your system; the upsert key with provider.
membership_nostring or nullMembership or card number. Omit to keep the current one; null clears it.
full_namerequiredstring
phonestring or null
emailstring (email) or null
date_of_birthstring or null
identity_type"mykad" | "mypr" | "mykas" | "mytentera" | "passport" | "other"Inferred for a 12-digit MyKad when omitted.
identity_numberstring or null
Example requestJSON
{
  "provider": "partner_v1",
  "external_id": "MEM-0012345",
  "membership_no": "PM00012345",
  "full_name": "Siti Aminah binti Ahmad",
  "phone": "+60123456789",
  "email": "siti@example.com",
  "date_of_birth": "1990-01-01",
  "identity_type": "mykad",
  "identity_number": "900101-14-5678"
}

Responses

200An existing member was updated.Returns Member.

upsertMember 200 response
FieldTypeDescription
idrequiredstring (uuid)Clive member id.
statusrequired"active" | "inactive"
full_namerequiredstring or null
phonerequiredstring or null
emailrequiredstring or null
date_of_birthrequiredstring or nullYYYY-MM-DD.
identity_typerequired"mykad" | "mypr" | "mykas" | "mytentera" | "passport" | "other" | nullWhat identity_number is: MyKad, passport, ...
identity_numberrequiredstring or nullIC or passport number.
membershipsrequiredarray of Membership
providerrequiredstringPartner system the id belongs to, e.g. partner_v1.
external_idrequiredstringThe member's id in that system.
membership_norequiredstring or nullMembership or card number, when the partner has one.
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
Response 200JSON
{
  "id": "c41f7a52-8d1e-4b2a-9f3c-6e5d4c3b2a10",
  "status": "active",
  "full_name": "Siti Aminah binti Ahmad",
  "phone": "+60123456789",
  "email": "siti@example.com",
  "date_of_birth": "1990-01-01",
  "identity_type": "mykad",
  "identity_number": "900101-14-5678",
  "memberships": [
    {
      "provider": "partner_v1",
      "external_id": "MEM-0012345",
      "membership_no": "PM00012345"
    }
  ],
  "created_at": "2026-10-01T00:00:00.000Z",
  "updated_at": "2026-10-24T02:15:09.123Z"
}

201A new member was created.Returns Member.

upsertMember 201 response
FieldTypeDescription
idrequiredstring (uuid)Clive member id.
statusrequired"active" | "inactive"
full_namerequiredstring or null
phonerequiredstring or null
emailrequiredstring or null
date_of_birthrequiredstring or nullYYYY-MM-DD.
identity_typerequired"mykad" | "mypr" | "mykas" | "mytentera" | "passport" | "other" | nullWhat identity_number is: MyKad, passport, ...
identity_numberrequiredstring or nullIC or passport number.
membershipsrequiredarray of Membership
providerrequiredstringPartner system the id belongs to, e.g. partner_v1.
external_idrequiredstringThe member's id in that system.
membership_norequiredstring or nullMembership or card number, when the partner has one.
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
Response 201JSON
{
  "id": "c41f7a52-8d1e-4b2a-9f3c-6e5d4c3b2a10",
  "status": "active",
  "full_name": "Siti Aminah binti Ahmad",
  "phone": "+60123456789",
  "email": "siti@example.com",
  "date_of_birth": "1990-01-01",
  "identity_type": "mykad",
  "identity_number": "900101-14-5678",
  "memberships": [
    {
      "provider": "partner_v1",
      "external_id": "MEM-0012345",
      "membership_no": "PM00012345"
    }
  ],
  "created_at": "2026-10-01T00:00:00.000Z",
  "updated_at": "2026-10-24T02:15:09.123Z"
}
upsertMember error responses
StatusMeaning
400Malformed request, invalid cursor, or missing Idempotency-Key (IDEMPOTENCY_KEY_REQUIRED).
401Missing, invalid or expired access token, or the API client was revoked (INVALID_TOKEN).
403The token lacks the scope this operation needs (INSUFFICIENT_SCOPE).
409Conflict, or a request with the same Idempotency-Key is still in flight (IDEMPOTENCY_IN_PROGRESS).
422Validation failed (VALIDATION_ERROR), or the Idempotency-Key was used for a different request (IDEMPOTENCY_KEY_REUSED).
429Per-client rate limit exceeded (RATE_LIMITED).
500Unexpected error (INTERNAL_ERROR).

Every error body is the error envelope; branch on its code.

Get a member

GET/public/v1/members/{id}

Scope members:readOperation getMember

Requires scope members:read.

Parameters

getMember parameters
NameInTypeDescription
idrequiredpathstring (uuid)Clive member id.

Responses

200The member.Returns Member.

getMember 200 response
FieldTypeDescription
idrequiredstring (uuid)Clive member id.
statusrequired"active" | "inactive"
full_namerequiredstring or null
phonerequiredstring or null
emailrequiredstring or null
date_of_birthrequiredstring or nullYYYY-MM-DD.
identity_typerequired"mykad" | "mypr" | "mykas" | "mytentera" | "passport" | "other" | nullWhat identity_number is: MyKad, passport, ...
identity_numberrequiredstring or nullIC or passport number.
membershipsrequiredarray of Membership
providerrequiredstringPartner system the id belongs to, e.g. partner_v1.
external_idrequiredstringThe member's id in that system.
membership_norequiredstring or nullMembership or card number, when the partner has one.
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
Response 200JSON
{
  "id": "c41f7a52-8d1e-4b2a-9f3c-6e5d4c3b2a10",
  "status": "active",
  "full_name": "Siti Aminah binti Ahmad",
  "phone": "+60123456789",
  "email": "siti@example.com",
  "date_of_birth": "1990-01-01",
  "identity_type": "mykad",
  "identity_number": "900101-14-5678",
  "memberships": [
    {
      "provider": "partner_v1",
      "external_id": "MEM-0012345",
      "membership_no": "PM00012345"
    }
  ],
  "created_at": "2026-10-01T00:00:00.000Z",
  "updated_at": "2026-10-24T02:15:09.123Z"
}
getMember error responses
StatusMeaning
400Malformed request or invalid cursor.
401Missing, invalid or expired access token, or the API client was revoked (INVALID_TOKEN).
403The token lacks the scope this operation needs (INSUFFICIENT_SCOPE).
404No such record in this clinic (NOT_FOUND).
422Validation failed (VALIDATION_ERROR).
429Per-client rate limit exceeded (RATE_LIMITED).
500Unexpected error (INTERNAL_ERROR).

Every error body is the error envelope; branch on its code.

Update a member

PATCH/public/v1/members/{id}

Scope members:writeOperation updateMember

Requires scope members:write.

Parameters

updateMember parameters
NameInTypeDescription
idrequiredpathstring (uuid)Clive member id.
Idempotency-KeyheaderstringUnique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours.

Request body MemberPatch

Only the fields sent are changed; null clears an optional field.

updateMember request body
FieldTypeDescription
full_namestring
phonestring or null
emailstring (email) or null
date_of_birthstring or null
identity_type"mykad" | "mypr" | "mykas" | "mytentera" | "passport" | "other"Inferred for a 12-digit MyKad when omitted.
identity_numberstring or null
status"active" | "inactive"
Example requestJSON
{
  "full_name": "Siti Aminah binti Ahmad",
  "phone": "+60123456789",
  "email": "siti@example.com",
  "date_of_birth": "1990-01-01",
  "identity_type": "mykad",
  "identity_number": "900101-14-5678",
  "status": "active"
}

Responses

200The updated member.Returns Member.

updateMember 200 response
FieldTypeDescription
idrequiredstring (uuid)Clive member id.
statusrequired"active" | "inactive"
full_namerequiredstring or null
phonerequiredstring or null
emailrequiredstring or null
date_of_birthrequiredstring or nullYYYY-MM-DD.
identity_typerequired"mykad" | "mypr" | "mykas" | "mytentera" | "passport" | "other" | nullWhat identity_number is: MyKad, passport, ...
identity_numberrequiredstring or nullIC or passport number.
membershipsrequiredarray of Membership
providerrequiredstringPartner system the id belongs to, e.g. partner_v1.
external_idrequiredstringThe member's id in that system.
membership_norequiredstring or nullMembership or card number, when the partner has one.
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
Response 200JSON
{
  "id": "c41f7a52-8d1e-4b2a-9f3c-6e5d4c3b2a10",
  "status": "active",
  "full_name": "Siti Aminah binti Ahmad",
  "phone": "+60123456789",
  "email": "siti@example.com",
  "date_of_birth": "1990-01-01",
  "identity_type": "mykad",
  "identity_number": "900101-14-5678",
  "memberships": [
    {
      "provider": "partner_v1",
      "external_id": "MEM-0012345",
      "membership_no": "PM00012345"
    }
  ],
  "created_at": "2026-10-01T00:00:00.000Z",
  "updated_at": "2026-10-24T02:15:09.123Z"
}
updateMember error responses
StatusMeaning
400Malformed request or invalid cursor.
401Missing, invalid or expired access token, or the API client was revoked (INVALID_TOKEN).
403The token lacks the scope this operation needs (INSUFFICIENT_SCOPE).
404No such record in this clinic (NOT_FOUND).
409Conflict, or a request with the same Idempotency-Key is still in flight (IDEMPOTENCY_IN_PROGRESS).
422Validation failed (VALIDATION_ERROR), or the Idempotency-Key was used for a different request (IDEMPOTENCY_KEY_REUSED).
429Per-client rate limit exceeded (RATE_LIMITED).
500Unexpected error (INTERNAL_ERROR).

Every error body is the error envelope; branch on its code.

Find a member by membership number

GET/public/v1/members/by-membership/{membership_no}

Scope members:readOperation getMemberByMembership

Exact match. When the number is held under several providers, pass provider; an ambiguous match answers 409 MEMBERSHIP_AMBIGUOUS.

Requires scope members:read.

Parameters

getMemberByMembership parameters
NameInTypeDescription
membership_norequiredpathstring
providerquerystring

Responses

200The member.Returns Member.

getMemberByMembership 200 response
FieldTypeDescription
idrequiredstring (uuid)Clive member id.
statusrequired"active" | "inactive"
full_namerequiredstring or null
phonerequiredstring or null
emailrequiredstring or null
date_of_birthrequiredstring or nullYYYY-MM-DD.
identity_typerequired"mykad" | "mypr" | "mykas" | "mytentera" | "passport" | "other" | nullWhat identity_number is: MyKad, passport, ...
identity_numberrequiredstring or nullIC or passport number.
membershipsrequiredarray of Membership
providerrequiredstringPartner system the id belongs to, e.g. partner_v1.
external_idrequiredstringThe member's id in that system.
membership_norequiredstring or nullMembership or card number, when the partner has one.
created_atrequiredstring (date-time)
updated_atrequiredstring (date-time)
Response 200JSON
{
  "id": "c41f7a52-8d1e-4b2a-9f3c-6e5d4c3b2a10",
  "status": "active",
  "full_name": "Siti Aminah binti Ahmad",
  "phone": "+60123456789",
  "email": "siti@example.com",
  "date_of_birth": "1990-01-01",
  "identity_type": "mykad",
  "identity_number": "900101-14-5678",
  "memberships": [
    {
      "provider": "partner_v1",
      "external_id": "MEM-0012345",
      "membership_no": "PM00012345"
    }
  ],
  "created_at": "2026-10-01T00:00:00.000Z",
  "updated_at": "2026-10-24T02:15:09.123Z"
}
getMemberByMembership error responses
StatusMeaning
400Malformed request or invalid cursor.
401Missing, invalid or expired access token, or the API client was revoked (INVALID_TOKEN).
403The token lacks the scope this operation needs (INSUFFICIENT_SCOPE).
404No such record in this clinic (NOT_FOUND).
422Validation failed (VALIDATION_ERROR).
429Per-client rate limit exceeded (RATE_LIMITED).
500Unexpected error (INTERNAL_ERROR).

Every error body is the error envelope; branch on its code.