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.

Purpose codeShown to the member as
careCare. 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_emailNews and offers by email. Send me news, health tips and offers by email.
marketing_smsNews and offers by SMS. Send me news, health tips and offers by SMS.
marketing_whatsappNews 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.

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.