Data and trust
Consent
The consent purposes Clive records and the consent gate that every share of member data with a partner passes.
Clive records each member's consent per purpose, with the privacy notice version it was given under, how it was captured and when. Partner data sharing checks these records live, every time.
Consent purposes
| Purpose code | Shown to the member as |
|---|---|
care | Care. Use my information to provide and coordinate my care. |
share_results_<provider> | Share results with the partner. Send my released test results to my account with the partner. |
marketing_email | News and offers by email. Send me news, health tips and offers by email. |
marketing_sms | News and offers by SMS. Send me news, health tips and offers by SMS. |
marketing_whatsapp | News and offers on WhatsApp. Send me news, health tips and offers on WhatsApp. |
Partner purposes follow the pattern share_results_<provider>. Any lowercase slug is accepted as a purpose code, so a new partner purpose needs no API change. Partners send consent changes as consent.changed events and receive Clive's as the consent.changed webhook.
The consent gate
Every send of member data to a partner passes one consent gate, and every decision is logged on the patient (Consent tab › Sharing) with the purpose, consent status and notice version it relied on.
| Outbound event | Purpose | Needs |
|---|---|---|
result.released |
share_results_<provider> |
An explicit grant |
member.updated, member.merged |
care |
Not withdrawn |
consent.changed |
not gated | Always sent, so a withdrawal reaches the partner |
appointment.*, voucher.redeemed, lead.status_changed, case.status_changed |
not gated | Ids and statuses of the partner's own bookings, vouchers and enquiries |
| Booking write-back, availability, payment requests | not gated | The partner's own bookings and branches, and an amount it is asked to collect |
Result share (POST {base}/results) |
share_results_<provider> |
An explicit grant for full; otherwise only the notice |
GET /public/v1/members/{id}/results |
share_results_<provider> |
An explicit grant; else 403 CONSENT_REQUIRED |
GET /public/v1/members/{id}, /members/by-membership/{no} |
care |
Not withdrawn; else 403 CONSENT_REQUIRED |
The gate applies to endpoints registered by a partner's API client; endpoints staff register in Settings are the clinic's own systems. It is checked at every delivery attempt: if the member withdraws between two retries, the next attempt is not made and the delivery ends as cancelled (consent_withdrawn). Public API reads of results and profiles use the same gate (assertConsent), which answers 403 CONSENT_REQUIRED.
Withdrawal propagates both ways within an hour: a withdrawal in Clive is effective at once (the gate reads it live) and goes out as consent.changed; a withdrawal at the partner arrives as consent.changed and is applied when processed; anything lost on either side is repaired by the 15-minute reconciliation.
Webhook deliveries
Deliveries to endpoints registered by a partner's API client pass the consent gate at every attempt. result.released needs the member's grant of share_results_<provider> (e.g. share_results_partner_v1); member.updated and member.merged need care not to be withdrawn. When consent is missing or withdrawn the delivery is not sent: it ends as cancelled with cancel_reason consent_withdrawn (or no_consent), no further attempts are made, and the endpoint's failure count is untouched. Every decision is logged on the member (Consent tab › Sharing). See partner-contract-v1.md.
FHIR reads
| Resource | Purpose checked | Rule |
|---|---|---|
| DiagnosticReport, Observation | share_results_<client provider> (e.g. share_results_partner_v1; share_results_partner for a client without a provider) |
explicit grant required |
| Patient, Appointment | care |
allowed unless withdrawn |
| Consent | not gated | a withdrawal must always reach the partner |
A read of one member's resource, or a search with patient= / subject=,
answers 403 CONSENT_REQUIRED when the gate refuses. A search across
members leaves out the members the gate refuses (the page may then hold
fewer than _count entries; keep following next). Every decision, allowed
or blocked, is written to data_share_log with event type
fhir.<Resource>.<read|search>. Consent is read live, so a withdrawal stops
the very next request.