{
  "openapi": "3.1.0",
  "info": {
    "title": "Clive Partner API",
    "version": "1.0.0",
    "termsOfService": "https://clivehealth.app/legal/terms",
    "contact": {
      "name": "Clive Labs Sdn Bhd",
      "url": "https://clivehealth.app/contact",
      "email": "hello@clivehealth.app"
    },
    "description": "The Clive public API for partner systems (v1), operated by Clive Labs Sdn Bhd. Guides, the event catalogue and the changelog are at https://clivehealth.app/developers.\n\nAuthentication: OAuth 2.0 client credentials at `POST /oauth/token`; send the access token as `Authorization: Bearer <token>`.\n\nConventions: snake_case JSON; every error is `{ code, message, details? }`; lists are cursor-paginated `{ data, next_cursor, has_more }` and accept `updated_since`; every POST needs an `Idempotency-Key` header (optional on PATCH and DELETE).\n\nRate limits: per API client per minute (default 600), reported in `X-RateLimit-*` headers; 429 with `Retry-After` when exceeded. See https://clivehealth.app/developers/rate-limits.\n\nVersioning: the major version is in the path (`/public/v1`). Changes within v1 are additive; breaking changes ship as a new major version, and deprecated operations get at least six months' notice via `Deprecation`, `Sunset` and `Link` headers. See https://clivehealth.app/developers/versioning and https://clivehealth.app/developers/changelog."
  },
  "servers": [
    {
      "url": "/public/v1",
      "description": "The host this document was served from (sandbox or production)."
    },
    {
      "url": "https://sandbox-api.clivehealth.app/public/v1",
      "description": "Sandbox: synthetic data, test credentials."
    },
    {
      "url": "https://api.clivehealth.app/public/v1",
      "description": "Production."
    }
  ],
  "externalDocs": {
    "url": "https://clivehealth.app/developers",
    "description": "Guides, webhooks, FHIR R4 and the changelog."
  },
  "tags": [
    {
      "name": "Account",
      "description": "The calling API client."
    },
    {
      "name": "Members",
      "description": "Members (patients) the partner shares with the clinic, keyed by the partner's own id."
    },
    {
      "name": "Venues",
      "description": "The clinic's branches."
    },
    {
      "name": "Appointments",
      "description": "Bookings at a venue."
    },
    {
      "name": "Availability",
      "description": "Bookable slots."
    },
    {
      "name": "Vouchers",
      "description": "Partner vouchers held by members, and their redemption at a venue."
    },
    {
      "name": "Results",
      "description": "Released lab results, behind the member's consent."
    }
  ],
  "components": {
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.0 client credentials (RFC 6749 §4.4). Exchange client_id and client_secret at /oauth/token for a Bearer access token. Each operation lists the scope it needs.",
        "flows": {
          "clientCredentials": {
            "tokenUrl": "/oauth/token",
            "scopes": {
              "members:read": "Read member profiles",
              "members:write": "Create and update member profiles",
              "consents:read": "Read consents",
              "consents:write": "Record and withdraw consents",
              "appointments:read": "Read appointments",
              "appointments:write": "Book, reschedule and cancel appointments",
              "availability:read": "Read available slots",
              "venues:read": "Read branches, hours and closures",
              "venues:write": "Update branch hours and closures",
              "vouchers:read": "Read vouchers and redemptions",
              "vouchers:write": "Issue and cancel vouchers",
              "results:read": "Read released results (consent permitting)",
              "leads:read": "Read enquiries",
              "leads:write": "Create and update enquiries",
              "cases:read": "Read cases",
              "cases:write": "Create and update cases",
              "webhooks:manage": "Manage webhook endpoints"
            }
          }
        }
      }
    },
    "headers": {
      "X-RateLimit-Limit": {
        "description": "Requests allowed per minute for this API client.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests left in the current one-minute window.",
        "schema": {
          "type": "integer"
        }
      },
      "X-RateLimit-Reset": {
        "description": "Seconds until the current window resets.",
        "schema": {
          "type": "integer"
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer"
        }
      },
      "Idempotent-Replayed": {
        "description": "Present and `true` when this response is a stored response replayed for a repeated Idempotency-Key.",
        "schema": {
          "type": "string",
          "enum": [
            "true"
          ]
        }
      },
      "Deprecation": {
        "description": "RFC 9745: present on deprecated operations, the time of deprecation (`@<unix seconds>`).",
        "schema": {
          "type": "string"
        }
      },
      "Sunset": {
        "description": "RFC 8594: present on deprecated operations, the HTTP-date after which the operation stops working.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, invalid or expired access token, or the API client was revoked (INVALID_TOKEN).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "The token lacks the scope this operation needs (INSUFFICIENT_SCOPE).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such record in this clinic (NOT_FOUND).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Per-client rate limit exceeded (RATE_LIMITED).",
        "headers": {
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/X-RateLimit-Limit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/X-RateLimit-Remaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/X-RateLimit-Reset"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected error (INTERNAL_ERROR).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code, e.g. VALIDATION_ERROR, NOT_FOUND, INSUFFICIENT_SCOPE."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation; may change, do not parse."
          },
          "details": {
            "description": "Code-specific detail. VALIDATION_ERROR: `{ fields: { <field>: [messages] } }`."
          }
        },
        "required": [
          "code",
          "message"
        ],
        "description": "Error envelope returned by every non-2xx response."
      },
      "ApiClientInfo": {
        "type": "object",
        "properties": {
          "client_id": {
            "type": "string"
          },
          "clinic_id": {
            "type": "string",
            "format": "uuid"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "members:read",
                "members:write",
                "consents:read",
                "consents:write",
                "appointments:read",
                "appointments:write",
                "availability:read",
                "venues:read",
                "venues:write",
                "vouchers:read",
                "vouchers:write",
                "results:read",
                "leads:read",
                "leads:write",
                "cases:read",
                "cases:write",
                "webhooks:manage"
              ]
            }
          }
        },
        "required": [
          "client_id",
          "clinic_id",
          "scopes"
        ]
      },
      "Membership": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "description": "Partner system the id belongs to, e.g. `partner_v1`."
          },
          "external_id": {
            "type": "string",
            "description": "The member's id in that system."
          },
          "membership_no": {
            "type": [
              "string",
              "null"
            ],
            "description": "Membership or card number, when the partner has one."
          }
        },
        "required": [
          "provider",
          "external_id",
          "membership_no"
        ]
      },
      "Member": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Clive member id."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ]
          },
          "full_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "date_of_birth": {
            "type": [
              "string",
              "null"
            ],
            "description": "YYYY-MM-DD."
          },
          "identity_type": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "mykad",
              "mypr",
              "mykas",
              "mytentera",
              "passport",
              "other",
              null
            ],
            "description": "What `identity_number` is: MyKad, passport, ..."
          },
          "identity_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "IC or passport number."
          },
          "memberships": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Membership"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "status",
          "full_name",
          "phone",
          "email",
          "date_of_birth",
          "identity_type",
          "identity_number",
          "memberships",
          "created_at",
          "updated_at"
        ],
        "description": "A member (patient). Clinical records are never part of this resource."
      },
      "MemberPage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page; null on the last page."
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "next_cursor",
          "has_more"
        ]
      },
      "MemberUpsert": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]{1,31}$",
            "description": "Your system's provider code, e.g. `partner_v1`."
          },
          "external_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "The member's id in your system; the upsert key with `provider`."
          },
          "membership_no": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 64,
            "description": "Membership or card number. Omit to keep the current one; null clears it."
          },
          "full_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 6,
            "maxLength": 32
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "format": "email"
          },
          "date_of_birth": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "identity_type": {
            "type": "string",
            "enum": [
              "mykad",
              "mypr",
              "mykas",
              "mytentera",
              "passport",
              "other"
            ],
            "description": "Inferred for a 12-digit MyKad when omitted."
          },
          "identity_number": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 3,
            "maxLength": 32
          }
        },
        "required": [
          "provider",
          "external_id",
          "full_name"
        ],
        "additionalProperties": false
      },
      "MemberPatch": {
        "type": "object",
        "properties": {
          "full_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 6,
            "maxLength": 32
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200,
            "format": "email"
          },
          "date_of_birth": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "identity_type": {
            "type": "string",
            "enum": [
              "mykad",
              "mypr",
              "mykas",
              "mytentera",
              "passport",
              "other"
            ],
            "description": "Inferred for a 12-digit MyKad when omitted."
          },
          "identity_number": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 3,
            "maxLength": 32
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive"
            ]
          }
        },
        "additionalProperties": false,
        "description": "Only the fields sent are changed; null clears an optional field."
      },
      "Venue": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "timezone": {
            "type": "string",
            "description": "IANA time zone, e.g. Asia/Kuala_Lumpur."
          },
          "is_default": {
            "type": "boolean",
            "description": "The clinic's main branch."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "address",
          "phone",
          "timezone",
          "is_default",
          "created_at",
          "updated_at"
        ],
        "description": "A branch of the clinic. Only active branches are listed."
      },
      "VenuePage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Venue"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page; null on the last page."
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "next_cursor",
          "has_more"
        ]
      },
      "Appointment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "venue_id": {
            "type": "string",
            "format": "uuid"
          },
          "practitioner_id": {
            "type": "string",
            "format": "uuid"
          },
          "start_time": {
            "type": "string",
            "format": "date-time"
          },
          "end_time": {
            "type": "string",
            "format": "date-time"
          },
          "duration_minutes": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "description": "Service or treatment type."
          },
          "status": {
            "type": "string",
            "enum": [
              "scheduled",
              "confirmed",
              "checked_in",
              "in_progress",
              "completed",
              "cancelled",
              "no_show"
            ]
          },
          "cancel_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ],
            "description": "How it was booked: `partner_api`, `online_booking`, `manual`, ..."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "member_id",
          "venue_id",
          "practitioner_id",
          "start_time",
          "end_time",
          "duration_minutes",
          "type",
          "status",
          "cancel_reason",
          "source",
          "created_at",
          "updated_at"
        ],
        "description": "A booking. Clinical notes are never part of this resource."
      },
      "AppointmentPage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Appointment"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page; null on the last page."
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "next_cursor",
          "has_more"
        ]
      },
      "AppointmentCreate": {
        "type": "object",
        "properties": {
          "member_id": {
            "type": "string",
            "format": "uuid"
          },
          "venue_id": {
            "type": "string",
            "format": "uuid"
          },
          "start_time": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 with offset; must be in the future."
          },
          "duration_minutes": {
            "type": "integer",
            "minimum": 5,
            "maximum": 480,
            "description": "Defaults to the clinic's slot length."
          },
          "type": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "practitioner_id": {
            "type": "string",
            "format": "uuid",
            "description": "A specific practitioner; otherwise the first free practitioner at the venue is assigned."
          }
        },
        "required": [
          "member_id",
          "venue_id",
          "start_time",
          "type"
        ],
        "additionalProperties": false
      },
      "AppointmentCancel": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          }
        },
        "additionalProperties": false
      },
      "Slot": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string",
            "format": "date-time"
          },
          "end": {
            "type": "string",
            "format": "date-time"
          },
          "available": {
            "type": "boolean",
            "description": "Some practitioner at the venue is free for the whole slot, and it has not started."
          }
        },
        "required": [
          "start",
          "end",
          "available"
        ]
      },
      "Availability": {
        "type": "object",
        "properties": {
          "venue_id": {
            "type": "string",
            "format": "uuid"
          },
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD in the venue's time zone."
          },
          "timezone": {
            "type": "string"
          },
          "slot_minutes": {
            "type": "integer"
          },
          "slots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Slot"
            },
            "description": "The day's slot grid; empty for a date in the past or beyond the booking horizon."
          }
        },
        "required": [
          "venue_id",
          "date",
          "timezone",
          "slot_minutes",
          "slots"
        ]
      },
      "VenueDay": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "description": "YYYY-MM-DD in the venue's time zone."
          },
          "closed": {
            "type": "boolean"
          },
          "closure": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "source": {
                "type": "string",
                "enum": [
                  "local",
                  "holiday",
                  "partner"
                ],
                "description": "Who closed it: the clinic, a partner, or a public holiday."
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "source",
              "reason"
            ]
          },
          "open": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "start": {
                  "type": "string",
                  "format": "date-time"
                },
                "end": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "required": [
                "start",
                "end"
              ]
            },
            "description": "Opening intervals as UTC instants; empty when closed."
          }
        },
        "required": [
          "date",
          "closed",
          "closure",
          "open"
        ]
      },
      "VenueAvailability": {
        "type": "object",
        "properties": {
          "venue_id": {
            "type": "string",
            "format": "uuid"
          },
          "timezone": {
            "type": "string"
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VenueDay"
            }
          }
        },
        "required": [
          "venue_id",
          "timezone",
          "from",
          "to",
          "days"
        ],
        "description": "Opening hours, closures and observed public holidays of a venue, day by day."
      },
      "VoucherRedemption": {
        "type": "object",
        "properties": {
          "redeemed_at": {
            "type": "string",
            "format": "date-time"
          },
          "venue_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "appointment_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "method": {
            "type": "string",
            "enum": [
              "qr",
              "code",
              "api"
            ]
          },
          "validation": {
            "type": "string",
            "enum": [
              "online",
              "offline",
              "none"
            ],
            "description": "`online`: confirmed with the provider; `offline`: the provider was unreachable and the clinic accepted it for later reconciliation."
          }
        },
        "required": [
          "redeemed_at",
          "venue_id",
          "appointment_id",
          "method",
          "validation"
        ]
      },
      "Voucher": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "member_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "provider": {
            "type": "string"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The partner's voucher id."
          },
          "code": {
            "type": "string"
          },
          "package_external_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "issued",
              "reserved",
              "redeemed",
              "void",
              "expired"
            ]
          },
          "valid_from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "valid_to": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "changes_used": {
            "type": "integer",
            "description": "Reschedules and cancellations of the voucher's booking so far."
          },
          "purchased_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "redemption": {
            "allOf": [
              {
                "$ref": "#/components/schemas/VoucherRedemption"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ]
          }
        },
        "required": [
          "id",
          "member_id",
          "provider",
          "external_id",
          "code",
          "package_external_id",
          "status",
          "valid_from",
          "valid_to",
          "changes_used",
          "purchased_at",
          "created_at",
          "updated_at",
          "redemption"
        ],
        "description": "A partner voucher held by a member."
      },
      "VoucherPage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Voucher"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Pass as `cursor` to fetch the next page; null on the last page."
          },
          "has_more": {
            "type": "boolean"
          }
        },
        "required": [
          "data",
          "next_cursor",
          "has_more"
        ]
      },
      "VoucherRedeem": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "minLength": 4,
            "maxLength": 128,
            "description": "The voucher code; case and spaces are ignored."
          },
          "venue_id": {
            "type": "string",
            "format": "uuid",
            "description": "The branch where the member is being seen."
          },
          "appointment_id": {
            "type": "string",
            "format": "uuid",
            "description": "The booking the voucher is used for; it is checked in. Defaults to the voucher's own booking within 12 hours of now."
          }
        },
        "required": [
          "code",
          "venue_id"
        ],
        "additionalProperties": false
      },
      "VoucherRedeemResult": {
        "type": "object",
        "properties": {
          "voucher": {
            "$ref": "#/components/schemas/Voucher"
          },
          "appointment_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "checked_in": {
            "type": "boolean",
            "description": "The appointment was moved to `checked_in` by this redemption."
          }
        },
        "required": [
          "voucher",
          "appointment_id",
          "checked_in"
        ]
      },
      "LabValue": {
        "type": "object",
        "properties": {
          "analyte": {
            "type": "string"
          },
          "value_numeric": {
            "type": [
              "number",
              "null"
            ]
          },
          "value_text": {
            "type": [
              "string",
              "null"
            ]
          },
          "units": {
            "type": [
              "string",
              "null"
            ]
          },
          "reference_low": {
            "type": [
              "number",
              "null"
            ]
          },
          "reference_high": {
            "type": [
              "number",
              "null"
            ]
          },
          "flag": {
            "type": [
              "string",
              "null"
            ],
            "description": "H, L, A or N against the reference range; display only, not a diagnosis."
          },
          "observed_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "analyte",
          "value_numeric",
          "value_text",
          "units",
          "reference_low",
          "reference_high",
          "flag",
          "observed_at"
        ]
      },
      "ReleasedResult": {
        "type": "object",
        "properties": {
          "result_id": {
            "type": "string",
            "format": "uuid",
            "description": "The lab order whose results were released."
          },
          "lab_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "released_at": {
            "type": "string",
            "format": "date-time"
          },
          "values": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LabValue"
            }
          },
          "report_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Signed link to the report PDF, valid for one hour; null without a PDF."
          },
          "report_url_expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "result_id",
          "lab_name",
          "released_at",
          "values",
          "report_url",
          "report_url_expires_at"
        ],
        "description": "Results a doctor has released to the member. Unreleased or withheld results are never returned."
      },
      "MemberResults": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReleasedResult"
            }
          }
        },
        "required": [
          "data"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "summary": "The calling API client",
        "description": "Checks a token: the client, its clinic and the scopes the token carries. Any valid token may call it.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "oauth2": []
          }
        ],
        "responses": {
          "200": {
            "description": "The client.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClientInfo"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/members": {
      "get": {
        "operationId": "listMembers",
        "summary": "List members",
        "description": "Active and inactive members, ordered by `updated_at` then `id`.\n\nRequires scope `members:read`.",
        "tags": [
          "Members"
        ],
        "security": [
          {
            "oauth2": [
              "members:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Page size, 1 to 200."
            },
            "required": false,
            "description": "Page size, 1 to 200.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512,
              "description": "Opaque cursor from the previous page's `next_cursor`."
            },
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "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."
            },
            "required": false,
            "description": "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.",
            "name": "updated_since",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64,
              "description": "Exact membership number."
            },
            "required": false,
            "description": "Exact membership number.",
            "name": "membership_no",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "inactive"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of members.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemberPage"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "upsertMember",
        "summary": "Create or update a member by external id",
        "description": "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.\n\nRequires scope `members:write`.",
        "tags": [
          "Members"
        ],
        "security": [
          {
            "oauth2": [
              "members:write"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours."
            },
            "required": true,
            "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemberUpsert"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "An existing member was updated.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "201": {
            "description": "A new member was created.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request, invalid cursor, or missing Idempotency-Key (IDEMPOTENCY_KEY_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Conflict, or a request with the same Idempotency-Key is still in flight (IDEMPOTENCY_IN_PROGRESS).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR), or the Idempotency-Key was used for a different request (IDEMPOTENCY_KEY_REUSED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/members/{id}": {
      "get": {
        "operationId": "getMember",
        "summary": "Get a member",
        "description": "Requires scope `members:read`.",
        "tags": [
          "Members"
        ],
        "security": [
          {
            "oauth2": [
              "members:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Clive member id."
            },
            "required": true,
            "description": "Clive member id.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The member.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "operationId": "updateMember",
        "summary": "Update a member",
        "description": "Requires scope `members:write`.",
        "tags": [
          "Members"
        ],
        "security": [
          {
            "oauth2": [
              "members:write"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Clive member id."
            },
            "required": true,
            "description": "Clive member id.",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours."
            },
            "required": false,
            "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MemberPatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated member.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict, or a request with the same Idempotency-Key is still in flight (IDEMPOTENCY_IN_PROGRESS).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR), or the Idempotency-Key was used for a different request (IDEMPOTENCY_KEY_REUSED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/members/by-membership/{membership_no}": {
      "get": {
        "operationId": "getMemberByMembership",
        "summary": "Find a member by membership number",
        "description": "Exact match. When the number is held under several providers, pass `provider`; an ambiguous match answers 409 MEMBERSHIP_AMBIGUOUS.\n\nRequires scope `members:read`.",
        "tags": [
          "Members"
        ],
        "security": [
          {
            "oauth2": [
              "members:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 64
            },
            "required": true,
            "name": "membership_no",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]{1,31}$"
            },
            "required": false,
            "name": "provider",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The member.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/venues": {
      "get": {
        "operationId": "listVenues",
        "summary": "List venues",
        "description": "The clinic's active branches, ordered by `updated_at` then `id`.\n\nRequires scope `venues:read`.",
        "tags": [
          "Venues"
        ],
        "security": [
          {
            "oauth2": [
              "venues:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Page size, 1 to 200."
            },
            "required": false,
            "description": "Page size, 1 to 200.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512,
              "description": "Opaque cursor from the previous page's `next_cursor`."
            },
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "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."
            },
            "required": false,
            "description": "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.",
            "name": "updated_since",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of venues.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VenuePage"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/venues/{id}": {
      "get": {
        "operationId": "getVenue",
        "summary": "Get a venue",
        "description": "Requires scope `venues:read`.",
        "tags": [
          "Venues"
        ],
        "security": [
          {
            "oauth2": [
              "venues:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The venue.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Venue"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/appointments": {
      "get": {
        "operationId": "listAppointments",
        "summary": "List appointments",
        "description": "Ordered by `updated_at` then `id`. `from` and `to` filter on `start_time`.\n\nRequires scope `appointments:read`.",
        "tags": [
          "Appointments"
        ],
        "security": [
          {
            "oauth2": [
              "appointments:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Page size, 1 to 200."
            },
            "required": false,
            "description": "Page size, 1 to 200.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512,
              "description": "Opaque cursor from the previous page's `next_cursor`."
            },
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "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."
            },
            "required": false,
            "description": "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.",
            "name": "updated_since",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": false,
            "name": "member_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": false,
            "name": "venue_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Appointments starting at or after this time."
            },
            "required": false,
            "description": "Appointments starting at or after this time.",
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "Appointments starting before this time."
            },
            "required": false,
            "description": "Appointments starting before this time.",
            "name": "to",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "scheduled",
                "confirmed",
                "checked_in",
                "in_progress",
                "completed",
                "cancelled",
                "no_show"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of appointments.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppointmentPage"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "operationId": "createAppointment",
        "summary": "Book an appointment",
        "description": "Books the member at the venue. 409 SLOT_TAKEN when the practitioner (or every practitioner at the venue) is busy.\n\nRequires scope `appointments:write`.",
        "tags": [
          "Appointments"
        ],
        "security": [
          {
            "oauth2": [
              "appointments:write"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours."
            },
            "required": true,
            "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppointmentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new appointment.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appointment"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request, invalid cursor, or missing Idempotency-Key (IDEMPOTENCY_KEY_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Conflict, or a request with the same Idempotency-Key is still in flight (IDEMPOTENCY_IN_PROGRESS).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR), or the Idempotency-Key was used for a different request (IDEMPOTENCY_KEY_REUSED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/appointments/{id}": {
      "get": {
        "operationId": "getAppointment",
        "summary": "Get an appointment",
        "description": "Requires scope `appointments:read`.",
        "tags": [
          "Appointments"
        ],
        "security": [
          {
            "oauth2": [
              "appointments:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The appointment.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appointment"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/appointments/{id}/cancel": {
      "post": {
        "operationId": "cancelAppointment",
        "summary": "Cancel an appointment",
        "description": "Cancelling an already-cancelled appointment returns it unchanged. Appointments in progress or completed can't be cancelled (409).\n\nRequires scope `appointments:write`.",
        "tags": [
          "Appointments"
        ],
        "security": [
          {
            "oauth2": [
              "appointments:write"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours."
            },
            "required": true,
            "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppointmentCancel"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The cancelled appointment.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appointment"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request, invalid cursor, or missing Idempotency-Key (IDEMPOTENCY_KEY_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Conflict, or a request with the same Idempotency-Key is still in flight (IDEMPOTENCY_IN_PROGRESS).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR), or the Idempotency-Key was used for a different request (IDEMPOTENCY_KEY_REUSED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/venues/{id}/availability": {
      "get": {
        "operationId": "getVenueAvailability",
        "summary": "A venue's opening days",
        "description": "Day by day: open intervals, or why the day is closed. At most 62 days per request; `from` defaults to today at the venue and `to` to 27 days later.\n\nRequires scope `availability:read`.",
        "tags": [
          "Availability"
        ],
        "security": [
          {
            "oauth2": [
              "availability:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "required": false,
            "name": "from",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
            },
            "required": false,
            "name": "to",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The venue's days.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VenueAvailability"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/availability": {
      "get": {
        "operationId": "getAvailability",
        "summary": "Bookable slots at a venue on a day",
        "description": "Requires scope `availability:read`.",
        "tags": [
          "Availability"
        ],
        "security": [
          {
            "oauth2": [
              "availability:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "venue_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
              "description": "YYYY-MM-DD."
            },
            "required": true,
            "description": "YYYY-MM-DD.",
            "name": "date",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "The day's slots.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Availability"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/vouchers": {
      "get": {
        "operationId": "listVouchers",
        "summary": "List vouchers",
        "description": "Ordered by `updated_at` then `id`.\n\nRequires scope `vouchers:read`.",
        "tags": [
          "Vouchers"
        ],
        "security": [
          {
            "oauth2": [
              "vouchers:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50,
              "description": "Page size, 1 to 200."
            },
            "required": false,
            "description": "Page size, 1 to 200.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512,
              "description": "Opaque cursor from the previous page's `next_cursor`."
            },
            "required": false,
            "description": "Opaque cursor from the previous page's `next_cursor`.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "date-time",
              "description": "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."
            },
            "required": false,
            "description": "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.",
            "name": "updated_since",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": false,
            "name": "member_id",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "issued",
                "reserved",
                "redeemed",
                "void",
                "expired"
              ]
            },
            "required": false,
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of vouchers.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoucherPage"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/vouchers/{id}": {
      "get": {
        "operationId": "getVoucher",
        "summary": "Get a voucher",
        "description": "Requires scope `vouchers:read`.",
        "tags": [
          "Vouchers"
        ],
        "security": [
          {
            "oauth2": [
              "vouchers:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The voucher.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Voucher"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/vouchers/redeem": {
      "post": {
        "operationId": "redeemVoucher",
        "summary": "Redeem a voucher",
        "description": "Checks and redeems the voucher in one step, and checks the linked appointment in. A voucher is redeemed once: a second redeem is 409 `VOUCHER_ALREADY_REDEEMED` with `details` naming the venue, the person or client and the time. 404 `VOUCHER_NOT_FOUND`; 409 `VOUCHER_NOT_REDEEMABLE` (void, expired, not yet valid); 422 `VOUCHER_REJECTED` when the provider refused it online; 503 `VOUCHER_PROVIDER_UNAVAILABLE` when it could not be reached and the clinic refuses offline redemptions.\n\nRequires scope `vouchers:write`.",
        "tags": [
          "Vouchers"
        ],
        "security": [
          {
            "oauth2": [
              "vouchers:write"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours."
            },
            "required": true,
            "description": "Unique key for this operation (a UUID is a good choice). Retrying with the same key and request replays the first response for 24 hours.",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VoucherRedeem"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The redeemed voucher.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              },
              "Idempotent-Replayed": {
                "$ref": "#/components/headers/Idempotent-Replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VoucherRedeemResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request, invalid cursor, or missing Idempotency-Key (IDEMPOTENCY_KEY_REQUIRED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "Conflict, or a request with the same Idempotency-Key is still in flight (IDEMPOTENCY_IN_PROGRESS).",
            "headers": {
              "Retry-After": {
                "$ref": "#/components/headers/Retry-After"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR), or the Idempotency-Key was used for a different request (IDEMPOTENCY_KEY_REUSED).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/members/{id}/results": {
      "get": {
        "operationId": "getMemberResults",
        "summary": "Released results of a member",
        "description": "Newest first, at most 50. Needs the member's consent to share results with you (`share_results_<your provider>`, e.g. `share_results_partner_v1`): without it the answer is 403 CONSENT_REQUIRED. Every read is logged on the member's sharing log.\n\nRequires scope `results:read`.",
        "tags": [
          "Results"
        ],
        "security": [
          {
            "oauth2": [
              "results:read"
            ]
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "description": "Clive member id."
            },
            "required": true,
            "description": "Clive member id.",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "The member's released results.",
            "headers": {
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MemberResults"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or invalid cursor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "Validation failed (VALIDATION_ERROR).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "webhooks": {}
}
