openapi: 3.1.0
info:
  title: CuraeAI Platform API
  version: 1.0.0
  description: |
    Public CuraeAI Platform API. Server-to-server REST surface authenticated
    with CuraeAI-owned API keys (`cae_live_...` / `cae_test_...`).

    This spec is the single source of truth for the externally distributed
    `@curaeai/platform-types` package and the `@curaeai/platform-sdk` client.
    It deliberately excludes the cookie-authenticated internal app surface
    so that customers consuming the SDK cannot discover or accidentally
    type-check against private routes.
servers:
  - url: https://api.curaeai.com
    description: Production
  - url: https://sandbox.curaeai.com
    description: Sandbox (test-mode keys only)
  - url: http://localhost:3000
    description: Local development
security:
  - platformApiKey: []
paths:
  /api/platform/v1/patients/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
          format: uuid
        description: Patient identifier.
    get:
      summary: Retrieve a Platform patient profile
      description: |
        Retrieves the patient profile resource visible to the API key. The
        route enforces, in order: API-key scope (`patient:read`), Platform
        authorization (SpiceDB tuple-constraint resolution), tenant
        membership, and an RLS-backed Prisma read. Authorization-layer
        denials — including cross-tenant access attempts — surface as
        `403 PLATFORM_ACCESS_DENIED` with a uniform message that does not
        reveal whether the patient exists in another tenant. Resources
        that pass authorization but are absent from the caller's tenant
        via the RLS-backed read resolve as `404 NOT_FOUND`. See
        `Docs/architecture/PLATFORM_API_ARCHITECTURE.md` §7.4 for the
        tenant-leak-safe error contract.
      tags:
        - platform-v1
        - Platform Patients
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: If-None-Match
          required: false
          schema:
            type: string
          description: Optional weak or strong entity tag. Matching validators return `304 Not Modified`.
      responses:
        '200':
          description: Platform patient profile
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: &ref_0
                type: string
            Cache-Control:
              description: Cache policy applied to the response.
              schema: &ref_1
                type: string
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: &ref_2
                type: integer
                minimum: 0
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: &ref_3
                type: integer
                minimum: 0
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: &ref_4
                type: integer
                minimum: 0
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: &ref_5
                type: integer
                minimum: 1
            ETag:
              description: Strong entity tag matching the response `_version`.
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                required: &ref_43
                  - id
                  - resourceType
                  - givenName
                  - familyName
                  - birthDate
                  - createdAt
                  - updatedAt
                  - _version
                  - _links
                properties: &ref_44
                  id:
                    type: string
                    format: uuid
                  resourceType:
                    type: string
                    const: Patient
                  givenName:
                    type: string
                    minLength: 1
                  familyName:
                    type: string
                    minLength: 1
                  birthDate:
                    type: string
                    format: date
                  sex:
                    type: string
                  email:
                    type: string
                    format: email
                  phoneNumber:
                    type: string
                  preferredLanguage:
                    type: string
                  createdAt:
                    type: string
                    format: date-time
                  updatedAt:
                    type: string
                    format: date-time
                  _version:
                    type: string
                    description: |
                      Strong ETag-compatible version matching the response ETag
                      without the surrounding quotes. Derived from a byte-for-byte
                      content hash of the customer-visible projection, so
                      conditional `GET` requests with `If-None-Match` return
                      `304 Not Modified` only when the profile is byte-identical.
                  _links:
                    type: object
                    required: &ref_35
                      - self
                    properties: &ref_36
                      self:
                        type: object
                        required: &ref_37
                          - href
                        properties: &ref_38
                          href:
                            type: string
        '304':
          description: The patient profile has not changed since the supplied If-None-Match validator.
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            ETag:
              description: Current strong entity tag for the patient profile resource.
              schema:
                type: string
        default:
          description: RFC 7807 problem details
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            Retry-After:
              description: Seconds to wait before retrying a rate-limited request.
              schema: &ref_39
                type: integer
                minimum: 0
          content:
            application/problem+json:
              schema:
                type: object
                required: &ref_13
                  - type
                  - title
                  - status
                  - detail
                properties: &ref_14
                  type:
                    type: string
                  title:
                    type: string
                  status:
                    type: integer
                  detail:
                    type: string
                  instance:
                    type: string
                  code:
                    type: string
                  details:
                    type: object
                    additionalProperties: true
                  retryable:
                    type: boolean
  /api/sdk/v1/observations:search:
    post:
      summary: Search connection-scoped observations through the SDK gateway
      description: Returns a FHIR Bundle containing Observation resources authorized by the presented APP tenant API key with observation:read scope and the request body's `connectionId`. The gateway enforces APP tenant residency, SpiceDB app-install projection, subject lifecycle capability, resource-type scope, and service-level LOINC, sensitivity, and time-window scope before any PHI read is returned.
      tags:
        - sdk-v1
        - SDK Observations
      security:
        - platformApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - connectionId
              additionalProperties: false
              properties:
                connectionId:
                  type: string
                  format: uuid
                  description: Public SDK connection id returned by `POST /api/sdk/v1/connections`.
                loincCodes:
                  type: array
                  minItems: 1
                  maxItems: 256
                  items:
                    type: string
                    pattern: ^[0-9]{1,7}-[0-9]$
                fhirStatuses:
                  type: array
                  minItems: 1
                  description: FHIR R4 `observation-status` value-set codes (platform `FhirObservationStatus` enum). Defaults to `[FINAL, AMENDED, CORRECTED]` when omitted so the bundle does not surface `PRELIMINARY` / `CANCELLED` / `ENTERED_IN_ERROR` rows by accident.
                  items:
                    type: string
                    enum:
                      - REGISTERED
                      - PRELIMINARY
                      - FINAL
                      - AMENDED
                      - CORRECTED
                      - CANCELLED
                      - ENTERED_IN_ERROR
                      - UNKNOWN
                timeWindow:
                  type: object
                  additionalProperties: false
                  properties:
                    from:
                      type: string
                      format: date-time
                    to:
                      type: string
                      format: date-time
                sensitivityClasses:
                  type: array
                  minItems: 1
                  items:
                    type: string
                    enum:
                      - PATIENT_VISIBLE
                      - PSYCHOTHERAPY_NOTES
                      - PREVENTING_HARM
                      - PART_2_SUD
                      - STATE_MINOR_SENSITIVE
                      - THIRD_PARTY_CONFIDENTIAL
                      - SECURITY_OR_ABUSE_RISK
                      - APP_DERIVED_SEXUAL_HEALTH
                      - APP_DERIVED_BEHAVIORAL
                limit:
                  type: integer
                  minimum: 1
                  maximum: 500
                includeRawFhir:
                  type: boolean
      responses:
        '200':
          description: Connection-scoped FHIR Observation search bundle.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/fhir+json:
              schema:
                type: object
                required: &ref_11
                  - resourceType
                  - type
                additionalProperties: false
                description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Observation projections matched by the request scope.
                properties: &ref_12
                  resourceType:
                    type: string
                    enum:
                      - Bundle
                  type:
                    type: string
                    enum:
                      - searchset
                  total:
                    type: integer
                    minimum: 0
                  entry:
                    type: array
                    items:
                      type: object
                      required: &ref_50
                        - resource
                      additionalProperties: false
                      properties: &ref_51
                        fullUrl:
                          type: string
                          description: Tenant-anchored URN for the resource — stable across paginated responses but NOT a dereferenceable URL.
                          maxLength: 1024
                        resource:
                          type: object
                          required: &ref_48
                            - resourceType
                            - id
                            - status
                            - code
                            - subject
                          additionalProperties: true
                          description: 'Curae-shaped FHIR R4 Observation projection. Fields below are the contract; `additionalProperties: true` allows passthrough fields when the consumer requests `includeRawFhir: true`.'
                          properties: &ref_49
                            resourceType:
                              type: string
                              enum:
                                - Observation
                            id:
                              type: string
                              format: uuid
                            meta:
                              type: object
                              additionalProperties: true
                              description: 'FHIR `Meta`. The platform stamps `source` (a tenant-anchored URN) and `tag` (system + code + display for the SDK source kind). `includeRawFhir: true` requests may carry additional passthrough fields from the source FHIR resource — hence `additionalProperties: true`. The base contract guarantees `source` and `tag`.'
                              properties: &ref_45
                                source:
                                  type: string
                                  maxLength: 1024
                                  description: Tenant-anchored URN identifying the data source.
                                tag:
                                  type: array
                                  items:
                                    type: object
                                    required: &ref_6
                                      - system
                                      - code
                                    additionalProperties: false
                                    properties: &ref_7
                                      system:
                                        type: string
                                        maxLength: 512
                                        description: Code system URI (e.g., `http://loinc.org`).
                                      code:
                                        type: string
                                        maxLength: 128
                                      display:
                                        type: string
                                        maxLength: 256
                            status:
                              type: string
                              description: Normalized FHIR observation status — typically `final`, `preliminary`, `amended`, `corrected`, or `cancelled`. See `lib/sdk/observation-search.service.ts#normalizeObservationStatus`.
                              maxLength: 32
                            category:
                              type: array
                              items:
                                type: object
                                additionalProperties: false
                                properties: &ref_8
                                  coding:
                                    type: array
                                    items:
                                      type: object
                                      required: *ref_6
                                      additionalProperties: false
                                      properties: *ref_7
                                  text:
                                    type: string
                                    maxLength: 1024
                            code:
                              type: object
                              additionalProperties: false
                              properties: *ref_8
                            subject:
                              type: object
                              required:
                                - reference
                              additionalProperties: false
                              properties:
                                reference:
                                  type: string
                                  description: '`Patient/<uuid>` reference within the tenant.'
                                  maxLength: 256
                            effectiveDateTime:
                              type: string
                              format: date-time
                              description: When the observation was effective in the patient.
                            issued:
                              type: string
                              format: date-time
                              description: When the result was first issued by the lab / source.
                            valueQuantity:
                              type: object
                              additionalProperties: false
                              properties: &ref_9
                                value:
                                  type: number
                                  description: Numeric measurement value. Emitted only when the source row's `valueType` is QUANTITY.
                                unit:
                                  type: string
                                  maxLength: 128
                                  description: Human-readable unit (e.g., `mg/dL`).
                                system:
                                  type: string
                                  maxLength: 512
                                  description: Unit system URI (typically `http://unitsofmeasure.org`).
                                code:
                                  type: string
                                  maxLength: 128
                                  description: UCUM code matching `unit`.
                            valueString:
                              type: string
                              maxLength: 4096
                            interpretation:
                              type: array
                              items:
                                type: object
                                additionalProperties: false
                                properties: *ref_8
                            referenceRange:
                              type: array
                              items:
                                type: object
                                additionalProperties: false
                                properties: &ref_10
                                  low:
                                    type: object
                                    additionalProperties: false
                                    properties: *ref_9
                                  high:
                                    type: object
                                    additionalProperties: false
                                    properties: *ref_9
                                  text:
                                    type: string
                                    maxLength: 256
                            component:
                              type: array
                              items:
                                type: object
                                required: &ref_46
                                  - code
                                additionalProperties: false
                                properties: &ref_47
                                  code:
                                    type: object
                                    additionalProperties: false
                                    properties: *ref_8
                                  valueQuantity:
                                    type: object
                                    additionalProperties: false
                                    properties: *ref_9
                                  valueString:
                                    type: string
                                    maxLength: 4096
                                  valueBoolean:
                                    type: boolean
                                  interpretation:
                                    type: array
                                    items:
                                      type: object
                                      additionalProperties: false
                                      properties: *ref_8
                                  referenceRange:
                                    type: array
                                    items:
                                      type: object
                                      additionalProperties: false
                                      properties: *ref_10
                        search:
                          type: object
                          additionalProperties: false
                          properties:
                            mode:
                              type: string
                              enum:
                                - match
                                - include
                                - outcome
            application/json:
              schema:
                type: object
                required: *ref_11
                additionalProperties: false
                description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Observation projections matched by the request scope.
                properties: *ref_12
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/conditions:search:
    post:
      summary: Search connection-scoped conditions through the SDK gateway
      description: Returns a FHIR Bundle containing Condition resources authorized by the presented APP tenant API key with condition:read scope and the request body's `connectionId`. The gateway enforces APP tenant residency, SpiceDB app-install projection, subject lifecycle capability, resource-type scope, and service-level ICD-10/SNOMED, sensitivity, and time-window scope before any PHI read is returned. When `verificationStatuses` is omitted the platform defaults to `['confirmed']` so the bundle does not surface `entered-in-error` / `refuted` rows by accident.
      tags:
        - sdk-v1
        - SDK Conditions
      security:
        - platformApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - connectionId
              additionalProperties: false
              properties:
                connectionId:
                  type: string
                  format: uuid
                  description: Public SDK connection id returned by `POST /api/sdk/v1/connections`.
                conditionCodes:
                  type: array
                  minItems: 1
                  maxItems: 256
                  description: ICD-10-CM (e.g. `F90.0`) or SNOMED CT (numeric) codes.
                  items:
                    type: string
                    pattern: ^(?:[A-Z][0-9]{2}(?:\.[A-Z0-9]{1,4})?|[0-9]{6,18})$
                verificationStatuses:
                  type: array
                  minItems: 1
                  description: FHIR R4 `condition-ver-status` value-set codes. Defaults to `['confirmed']` when omitted.
                  items:
                    type: string
                    enum:
                      - unconfirmed
                      - provisional
                      - differential
                      - confirmed
                      - refuted
                      - entered-in-error
                clinicalStatuses:
                  type: array
                  minItems: 1
                  description: Platform `ClinicalConditionStatus` enum values.
                  items:
                    type: string
                    enum:
                      - ACTIVE
                      - RECURRENT
                      - REMISSION
                      - RESOLVED
                      - CHRONIC
                      - INACTIVE
                      - OTHER
                timeWindow:
                  type: object
                  additionalProperties: false
                  properties:
                    from:
                      type: string
                      format: date-time
                    to:
                      type: string
                      format: date-time
                sensitivityClasses:
                  type: array
                  minItems: 1
                  items:
                    type: string
                    enum:
                      - PATIENT_VISIBLE
                      - PSYCHOTHERAPY_NOTES
                      - PREVENTING_HARM
                      - PART_2_SUD
                      - STATE_MINOR_SENSITIVE
                      - THIRD_PARTY_CONFIDENTIAL
                      - SECURITY_OR_ABUSE_RISK
                      - APP_DERIVED_SEXUAL_HEALTH
                      - APP_DERIVED_BEHAVIORAL
                limit:
                  type: integer
                  minimum: 1
                  maximum: 500
                includeRawFhir:
                  type: boolean
      responses:
        '200':
          description: Connection-scoped FHIR Condition search bundle.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/fhir+json:
              schema:
                type: object
                required: &ref_15
                  - resourceType
                  - type
                additionalProperties: false
                description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Condition projections matched by the request scope.
                properties: &ref_16
                  resourceType:
                    type: string
                    enum:
                      - Bundle
                  type:
                    type: string
                    enum:
                      - searchset
                  total:
                    type: integer
                    minimum: 0
                  entry:
                    type: array
                    items:
                      type: object
                      required: &ref_55
                        - resource
                      additionalProperties: false
                      properties: &ref_56
                        fullUrl:
                          type: string
                          description: Tenant-anchored URN for the resource — stable across paginated responses but NOT a dereferenceable URL.
                          maxLength: 1024
                        resource:
                          type: object
                          required: &ref_53
                            - resourceType
                            - id
                            - subject
                          additionalProperties: true
                          description: 'Curae-shaped FHIR R4 Condition projection. Fields below are the contract; `additionalProperties: true` allows passthrough fields when the consumer requests `includeRawFhir: true`. See `lib/sdk/condition-search.service.ts#buildConditionResource` for the canonical projection.'
                          properties: &ref_54
                            resourceType:
                              type: string
                              enum:
                                - Condition
                            id:
                              type: string
                              format: uuid
                            meta:
                              type: object
                              additionalProperties: true
                              description: FHIR `Meta` for Condition projections. Same shape as `SdkFhirObservationMeta`; defined separately to keep the per-resource OpenAPI surfaces independently evolvable.
                              properties: &ref_52
                                source:
                                  type: string
                                  maxLength: 1024
                                  description: Tenant-anchored URN identifying the data source.
                                tag:
                                  type: array
                                  items:
                                    type: object
                                    required: *ref_6
                                    additionalProperties: false
                                    properties: *ref_7
                            clinicalStatus:
                              type: object
                              additionalProperties: false
                              properties: *ref_8
                            verificationStatus:
                              type: object
                              additionalProperties: false
                              properties: *ref_8
                            category:
                              type: array
                              items:
                                type: object
                                additionalProperties: false
                                properties: *ref_8
                            severity:
                              type: object
                              additionalProperties: false
                              properties: *ref_8
                            bodySite:
                              type: array
                              items:
                                type: object
                                additionalProperties: false
                                properties: *ref_8
                            code:
                              type: object
                              additionalProperties: false
                              properties: *ref_8
                            subject:
                              type: object
                              required:
                                - reference
                              additionalProperties: false
                              properties:
                                reference:
                                  type: string
                                  description: '`Patient/<uuid>` reference within the tenant.'
                                  maxLength: 256
                            onsetDateTime:
                              type: string
                              format: date-time
                              description: When the condition first manifested.
                            abatementDateTime:
                              type: string
                              format: date-time
                              description: When the condition resolved or went into remission.
                            recordedDate:
                              type: string
                              format: date-time
                              description: Date the condition was recorded by the source.
                            note:
                              type: array
                              items:
                                type: object
                                additionalProperties: true
                                properties:
                                  text:
                                    type: string
                                    maxLength: 8192
                        search:
                          type: object
                          additionalProperties: false
                          properties:
                            mode:
                              type: string
                              enum:
                                - match
                                - include
                                - outcome
            application/json:
              schema:
                type: object
                required: *ref_15
                additionalProperties: false
                description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Condition projections matched by the request scope.
                properties: *ref_16
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/immunizations:search:
    post:
      summary: Search connection-scoped immunizations through the SDK gateway
      description: "Returns a FHIR Bundle containing Immunization resources authorized by the presented APP tenant API key with immunization:read scope and the request body's `connectionId`. The gateway enforces APP tenant residency, SpiceDB app-install projection, subject lifecycle capability, resource-type scope, and service-level CVX, sensitivity, and time-window scope before any PHI read is returned. When `statuses` is omitted the platform defaults to `['COMPLETED']`; consumers needing the refusal signal can opt in with `statuses: ['COMPLETED', 'NOT_DONE']`."
      tags:
        - sdk-v1
        - SDK Immunizations
      security:
        - platformApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - connectionId
              additionalProperties: false
              properties:
                connectionId:
                  type: string
                  format: uuid
                  description: Public SDK connection id returned by `POST /api/sdk/v1/connections`.
                cvxCodes:
                  type: array
                  minItems: 1
                  maxItems: 64
                  description: CDC CVX numeric vaccine codes (1-4 digits).
                  items:
                    type: string
                    pattern: ^[0-9]{1,4}$
                statuses:
                  type: array
                  minItems: 1
                  description: Platform `ImmunizationStatus` enum values. Defaults to `['COMPLETED']` when omitted.
                  items:
                    type: string
                    enum:
                      - COMPLETED
                      - NOT_DONE
                      - ENTERED_IN_ERROR
                      - UNKNOWN
                timeWindow:
                  type: object
                  additionalProperties: false
                  properties:
                    from:
                      type: string
                      format: date-time
                    to:
                      type: string
                      format: date-time
                sensitivityClasses:
                  type: array
                  minItems: 1
                  items:
                    type: string
                    enum:
                      - PATIENT_VISIBLE
                      - PSYCHOTHERAPY_NOTES
                      - PREVENTING_HARM
                      - PART_2_SUD
                      - STATE_MINOR_SENSITIVE
                      - THIRD_PARTY_CONFIDENTIAL
                      - SECURITY_OR_ABUSE_RISK
                      - APP_DERIVED_SEXUAL_HEALTH
                      - APP_DERIVED_BEHAVIORAL
                limit:
                  type: integer
                  minimum: 1
                  maximum: 500
                includeRawFhir:
                  type: boolean
      responses:
        '200':
          description: Connection-scoped FHIR Immunization search bundle.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/fhir+json:
              schema:
                type: object
                required: &ref_17
                  - resourceType
                  - type
                additionalProperties: false
                description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Immunization projections matched by the request scope.
                properties: &ref_18
                  resourceType:
                    type: string
                    enum:
                      - Bundle
                  type:
                    type: string
                    enum:
                      - searchset
                  total:
                    type: integer
                    minimum: 0
                  entry:
                    type: array
                    items:
                      type: object
                      required: &ref_60
                        - resource
                      additionalProperties: false
                      properties: &ref_61
                        fullUrl:
                          type: string
                          description: Tenant-anchored URN for the resource — stable across paginated responses but NOT a dereferenceable URL.
                          maxLength: 1024
                        resource:
                          type: object
                          required: &ref_58
                            - resourceType
                            - id
                            - vaccineCode
                            - patient
                          additionalProperties: true
                          description: 'Curae-shaped FHIR R4 Immunization projection. Fields below are the contract; `additionalProperties: true` allows passthrough fields when the consumer requests `includeRawFhir: true`. See `lib/sdk/immunization-search.service.ts#buildImmunizationResource` for the canonical projection.'
                          properties: &ref_59
                            resourceType:
                              type: string
                              enum:
                                - Immunization
                            id:
                              type: string
                              format: uuid
                            meta:
                              type: object
                              additionalProperties: true
                              description: FHIR `Meta` for Immunization projections.
                              properties: &ref_57
                                source:
                                  type: string
                                  maxLength: 1024
                                  description: Tenant-anchored URN identifying the data source.
                                tag:
                                  type: array
                                  items:
                                    type: object
                                    required: *ref_6
                                    additionalProperties: false
                                    properties: *ref_7
                            status:
                              type: string
                              description: FHIR R4 `immunization-status` value-set code. Mapped from the platform's `ImmunizationStatus` enum.
                              enum:
                                - completed
                                - not-done
                                - entered-in-error
                              maxLength: 32
                            vaccineCode:
                              type: object
                              additionalProperties: false
                              properties: *ref_8
                            patient:
                              type: object
                              required:
                                - reference
                              additionalProperties: false
                              properties:
                                reference:
                                  type: string
                                  description: '`Patient/<uuid>` reference within the tenant.'
                                  maxLength: 256
                            occurrenceDateTime:
                              type: string
                              format: date-time
                              description: When the vaccine was administered or refused.
                            manufacturer:
                              type: object
                              additionalProperties: true
                              properties:
                                display:
                                  type: string
                                  maxLength: 256
                            lotNumber:
                              type: string
                              maxLength: 128
                        search:
                          type: object
                          additionalProperties: false
                          properties:
                            mode:
                              type: string
                              enum:
                                - match
                                - include
                                - outcome
            application/json:
              schema:
                type: object
                required: *ref_17
                additionalProperties: false
                description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Immunization projections matched by the request scope.
                properties: *ref_18
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/signals:
    post:
      summary: Ingest a connection-scoped SDK-derived signal
      description: "Persists an SDK-derived signal under the APP tenant when the presented API key has signal:write scope and the SDK connection allows the requested signal type. Signal payloads are limited to JSON objects no larger than 64 KiB and are validated against the namespace's payload schema (shallow event shape — scalars, shallow arrays/objects of scalars; SDK_SIGNAL_PAYLOAD_SCHEMA_MISMATCH on violation). Payloads MUST NOT contain PHI: no identifiers (names, emails, phone numbers, MRNs, external patient ids), free-text clinical notes, diagnoses, or document content — the subject is identified by the connection, and clinical facts belong on the FHIR surfaces. Requests must include Idempotency-Key for safe retries."
      tags:
        - sdk-v1
        - SDK Signals
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Required mutation idempotency key for SDK signal retries.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - connectionId
                - signalType
                - payload
              additionalProperties: false
              properties:
                connectionId:
                  type: string
                  format: uuid
                  description: Public SDK connection id returned by `POST /api/sdk/v1/connections`.
                signalType:
                  type: object
                  additionalProperties: false
                  required: &ref_19
                    - namespace
                    - code
                    - version
                  description: |-
                    Structured `(namespace, code, version)` tuple identifying an SDK-derived signal. Replaces the pre-W1 flat `AppDerivedSignalType` enum (`Docs/strategy/SDK_PRODUCT_FINDINGS.md` § 3 W1) so adding a new clinical / wellness domain is a platform-side registry addition rather than a Prisma enum migration.
                    Structurally a 1:1 projection of FHIR R4's `Coding` shape with simpler field names — the FHIR-canonical projection is `{system: 'https://curaeai.com/fhir/CodeSystem/sdk-signal/<namespace>', code: <code>, version: <version>}`. The simpler tuple is the developer-facing wire shape ("make it as easy as possible to develop medical apps", per the platform thesis); downstream FHIR servers and bulk exports project to the canonical form.
                    Consumer-app attribution is **not** part of the type — the emitting customer-account is captured by the API key's principal context and surfaced through audit logs and webhook payloads. Two different sexual-health apps emitting the same `{namespace: 'sti', code: 'exposure_event', version: 'v1'}` are emitting the same semantic signal and resolve to the same platform sensitivity classification.
                    Signals are for app-derived **events** with no EHR analog (exposure-notification dispatches, behavioural check-ins, self-asserted lifestyle facts). EHR-derivable questions ("does the user have a negative STI panel within 30 days?", "is the user up-to-date on CDC childhood vaccinations?") are **queries against the resource-search surface**, not signals — see `searchSdkObservations` / `searchSdkConditions` / `searchSdkImmunizations`.
                  properties: &ref_20
                    namespace:
                      type: string
                      minLength: 2
                      maxLength: 64
                      pattern: ^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$
                      description: Platform-owned semantic domain — `sti`, `behavioral`, `metabolic`, `cardiac`, etc. The platform validates each namespace against a closed registry (`lib/sdk/signal-namespace-policy.ts`); unknown namespaces are rejected at write time with `SDK_SIGNAL_NAMESPACE_UNKNOWN` because the platform owns the namespace → `PhiSensitivity` mapping (a HIPAA / Part-2 policy axis). Hierarchical dots are allowed for future taxonomic refinement (e.g. `behavioral.depression`).
                      example: sti
                    code:
                      type: string
                      minLength: 1
                      maxLength: 64
                      pattern: ^[a-z][a-z0-9_]*$
                      description: snake_case identifier within the namespace (e.g. `exposure_event`, `partner_disclosure_dispatched`, `check_in`).
                      example: exposure_event
                    version:
                      type: string
                      maxLength: 16
                      pattern: ^v[0-9]+(?:\.[0-9]+)?$
                      description: Semantic version of the `payload` schema bound to this tuple. Bumped when the payload contract changes incompatibly; minor `v<major>.<minor>` is reserved for additive changes.
                      example: v1
                payload:
                  type: object
                  additionalProperties: true
                observedAt:
                  type: string
                  format: date-time
      responses:
        '201':
          description: Signal created.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-Idempotency-Key:
              schema:
                type: string
            Idempotent-Replay:
              schema:
                type: string
                enum:
                  - 'true'
                  - 'false'
          content:
            application/json:
              schema:
                type: object
                required:
                  - id
                  - signalType
                  - sensitivity
                  - observedAt
                  - createdAt
                additionalProperties: false
                properties:
                  id:
                    type: string
                  signalType:
                    type: object
                    additionalProperties: false
                    required: *ref_19
                    description: |-
                      Structured `(namespace, code, version)` tuple identifying an SDK-derived signal. Replaces the pre-W1 flat `AppDerivedSignalType` enum (`Docs/strategy/SDK_PRODUCT_FINDINGS.md` § 3 W1) so adding a new clinical / wellness domain is a platform-side registry addition rather than a Prisma enum migration.
                      Structurally a 1:1 projection of FHIR R4's `Coding` shape with simpler field names — the FHIR-canonical projection is `{system: 'https://curaeai.com/fhir/CodeSystem/sdk-signal/<namespace>', code: <code>, version: <version>}`. The simpler tuple is the developer-facing wire shape ("make it as easy as possible to develop medical apps", per the platform thesis); downstream FHIR servers and bulk exports project to the canonical form.
                      Consumer-app attribution is **not** part of the type — the emitting customer-account is captured by the API key's principal context and surfaced through audit logs and webhook payloads. Two different sexual-health apps emitting the same `{namespace: 'sti', code: 'exposure_event', version: 'v1'}` are emitting the same semantic signal and resolve to the same platform sensitivity classification.
                      Signals are for app-derived **events** with no EHR analog (exposure-notification dispatches, behavioural check-ins, self-asserted lifestyle facts). EHR-derivable questions ("does the user have a negative STI panel within 30 days?", "is the user up-to-date on CDC childhood vaccinations?") are **queries against the resource-search surface**, not signals — see `searchSdkObservations` / `searchSdkConditions` / `searchSdkImmunizations`.
                    properties: *ref_20
                  sensitivity:
                    type: string
                  observedAt:
                    type: string
                    format: date-time
                  createdAt:
                    type: string
                    format: date-time
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/records/exports:
    post:
      summary: Queue a connection-scoped records export
      description: Creates a durable export job for authorized records covered by the SDK connection. The response contains job status and metadata; artifact materialization is asynchronous.
      tags:
        - sdk-v1
        - SDK Records
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Required idempotency key for safe records-export retries.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: &ref_62
                - connectionId
              additionalProperties: false
              properties: &ref_63
                connectionId:
                  type: string
                  format: uuid
                  description: Public SDK connection id returned by `POST /api/sdk/v1/connections`.
                resourceTypes:
                  type: array
                  minItems: 1
                  items:
                    type: string
                    enum: &ref_21
                      - Observation
                      - Condition
                      - Immunization
                format:
                  type: string
                  enum: &ref_22
                    - fhir_ndjson
                includeRawFhir:
                  type: boolean
      responses:
        '202':
          description: Records export job queued.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-Idempotency-Key:
              schema:
                type: string
            Idempotent-Replay:
              schema:
                type: string
                enum:
                  - 'true'
                  - 'false'
          content:
            application/json:
              schema:
                type: object
                required: &ref_23
                  - export
                additionalProperties: false
                properties: &ref_24
                  export:
                    type: object
                    required: &ref_25
                      - id
                      - connectionId
                      - status
                      - resourceTypes
                      - format
                      - includeRawFhir
                      - artifact
                      - errorCode
                      - queuedAt
                      - startedAt
                      - completedAt
                      - expiresAt
                      - failedAt
                      - createdAt
                      - updatedAt
                    additionalProperties: false
                    properties: &ref_26
                      id:
                        type: string
                        format: uuid
                      connectionId:
                        type: string
                        format: uuid
                      status:
                        type: string
                        enum: &ref_64
                          - QUEUED
                          - RUNNING
                          - READY
                          - FAILED
                          - EXPIRED
                      resourceTypes:
                        type: array
                        minItems: 1
                        items:
                          type: string
                          enum: *ref_21
                      format:
                        type: string
                        enum: *ref_22
                      includeRawFhir:
                        type: boolean
                      artifact:
                        type: &ref_65
                          - object
                          - 'null'
                        required: &ref_66
                          - sha256
                          - byteSize
                        additionalProperties: false
                        properties: &ref_67
                          sha256:
                            type: string
                          byteSize:
                            type: string
                      errorCode:
                        type:
                          - string
                          - 'null'
                      queuedAt:
                        type: string
                        format: date-time
                      startedAt:
                        type:
                          - string
                          - 'null'
                        format: date-time
                      completedAt:
                        type:
                          - string
                          - 'null'
                        format: date-time
                      expiresAt:
                        type:
                          - string
                          - 'null'
                        format: date-time
                      failedAt:
                        type:
                          - string
                          - 'null'
                        format: date-time
                      createdAt:
                        type: string
                        format: date-time
                      updatedAt:
                        type: string
                        format: date-time
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/records/exports/{exportId}:
    get:
      summary: Get a connection-scoped records export
      description: Returns status and metadata for a durable records export job created by the same SDK connection.
      tags:
        - sdk-v1
        - SDK Records
      security:
        - platformApiKey: []
      parameters:
        - in: path
          name: exportId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Records export job status and metadata.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/json:
              schema:
                type: object
                required: *ref_23
                additionalProperties: false
                properties: *ref_24
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/records/exports/{exportId}/download:
    get:
      summary: Download a connection-scoped records export artifact
      description: Redeems a `READY` records export as a short-lived signed URL for the materialized FHIR NDJSON artifact. The export must have been created by the same SDK connection. Returns `409` if the export exists but is not yet `READY` (still `QUEUED`/`RUNNING`) or is no longer downloadable (`EXPIRED`/`FAILED`), and `404` if it does not belong to the connection.
      tags:
        - sdk-v1
        - SDK Records
      security:
        - platformApiKey: []
      parameters:
        - in: path
          name: exportId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Signed download URL and export metadata.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/json:
              schema:
                type: object
                required: &ref_70
                  - download
                  - export
                additionalProperties: false
                properties: &ref_71
                  download:
                    type: object
                    description: Short-lived signed URL for a materialized records-export artifact. The URL is valid for `expiresInSeconds` and is the only way to retrieve the private artifact; redeem it promptly. The artifact's integrity hash and size are available on the accompanying `export` object.
                    required: &ref_68
                      - url
                      - expiresInSeconds
                      - expiresAt
                    additionalProperties: false
                    properties: &ref_69
                      url:
                        type: string
                        format: uri
                        description: Time-limited signed URL to download the FHIR NDJSON artifact.
                      expiresInSeconds:
                        type: integer
                        minimum: 60
                        description: Lifetime of the signed URL from issuance, in seconds.
                      expiresAt:
                        type: string
                        format: date-time
                        description: Absolute expiry timestamp of the signed URL.
                  export:
                    type: object
                    required: *ref_25
                    additionalProperties: false
                    properties: *ref_26
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/connections:
    post:
      summary: Create an SDK patient connection
      description: Starts a connection-oriented Curae Connect flow for the SDK app's authenticated user coordinate. The response returns a durable `connection.id` used as `connectionId` in subsequent SDK gateway request bodies. If user action is required, `nextAction` contains a Curae-hosted webview URL to open.
      tags:
        - sdk-v1
        - SDK Connections
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Required idempotency key for safe connection-create retries.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: &ref_73
                - appUserId
                - dataPackages
              additionalProperties: false
              properties: &ref_74
                appUserId:
                  type: string
                  minLength: 1
                  maxLength: 256
                dataPackages:
                  type: array
                  minItems: 1
                  description: Requested connection data packages. `signals` may be combined with record packages, but a connection must include at least one records package (`clinical_summary`, `labs`, `conditions`, or `immunizations`). When `signals` is present, `signalTypes` is required.
                  items:
                    type: string
                    enum: &ref_27
                      - clinical_summary
                      - labs
                      - conditions
                      - immunizations
                      - signals
                filters:
                  type: object
                  additionalProperties: false
                  properties: &ref_72
                    loincCodes:
                      type: array
                      minItems: 1
                      maxItems: 256
                      items:
                        type: string
                        pattern: ^[0-9]{1,7}-[0-9]$
                    conditionCodes:
                      type: array
                      minItems: 1
                      maxItems: 256
                      items:
                        type: string
                        pattern: ^(?:[A-Z][0-9]{2}(?:\.[A-Z0-9]{1,4})?|[0-9]{6,18})$
                    cvxCodes:
                      type: array
                      minItems: 1
                      maxItems: 256
                      items:
                        type: string
                        pattern: ^[0-9]{1,5}$
                    timeWindow:
                      type: object
                      additionalProperties: false
                      properties:
                        from:
                          type: string
                          format: date-time
                        to:
                          type: string
                          format: date-time
                    sensitivityClasses:
                      type: array
                      minItems: 1
                      maxItems: 16
                      items:
                        type: string
                        enum:
                          - PATIENT_VISIBLE
                          - PSYCHOTHERAPY_NOTES
                          - PREVENTING_HARM
                          - PART_2_SUD
                          - STATE_MINOR_SENSITIVE
                          - THIRD_PARTY_CONFIDENTIAL
                          - SECURITY_OR_ABUSE_RISK
                          - APP_DERIVED_SEXUAL_HEALTH
                          - APP_DERIVED_BEHAVIORAL
                signalTypes:
                  type: array
                  minItems: 1
                  maxItems: 32
                  items:
                    type: object
                    additionalProperties: false
                    required: *ref_19
                    description: |-
                      Structured `(namespace, code, version)` tuple identifying an SDK-derived signal. Replaces the pre-W1 flat `AppDerivedSignalType` enum (`Docs/strategy/SDK_PRODUCT_FINDINGS.md` § 3 W1) so adding a new clinical / wellness domain is a platform-side registry addition rather than a Prisma enum migration.
                      Structurally a 1:1 projection of FHIR R4's `Coding` shape with simpler field names — the FHIR-canonical projection is `{system: 'https://curaeai.com/fhir/CodeSystem/sdk-signal/<namespace>', code: <code>, version: <version>}`. The simpler tuple is the developer-facing wire shape ("make it as easy as possible to develop medical apps", per the platform thesis); downstream FHIR servers and bulk exports project to the canonical form.
                      Consumer-app attribution is **not** part of the type — the emitting customer-account is captured by the API key's principal context and surfaced through audit logs and webhook payloads. Two different sexual-health apps emitting the same `{namespace: 'sti', code: 'exposure_event', version: 'v1'}` are emitting the same semantic signal and resolve to the same platform sensitivity classification.
                      Signals are for app-derived **events** with no EHR analog (exposure-notification dispatches, behavioural check-ins, self-asserted lifestyle facts). EHR-derivable questions ("does the user have a negative STI panel within 30 days?", "is the user up-to-date on CDC childhood vaccinations?") are **queries against the resource-search surface**, not signals — see `searchSdkObservations` / `searchSdkConditions` / `searchSdkImmunizations`.
                    properties: *ref_20
                handoffReturnUrl:
                  type:
                    - string
                    - 'null'
                  format: uri
                  maxLength: 2048
                deviceKeyCnf:
                  type:
                    - object
                    - 'null'
                  additionalProperties: true
                refreshTokenHash:
                  type:
                    - string
                    - 'null'
                  pattern: ^[0-9a-f]{64}$
                sessionTtlSeconds:
                  type: integer
                  minimum: 60
                  maximum: 900
                authorizationTtlSeconds:
                  type: integer
                  minimum: 300
                  maximum: 31536000
                preferSignup:
                  type: boolean
      responses:
        '201':
          description: SDK connection created or recovered from idempotent replay.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/json:
              schema:
                type: object
                required: &ref_76
                  - connection
                  - nextAction
                additionalProperties: false
                properties: &ref_77
                  connection:
                    type: object
                    required: &ref_28
                      - id
                      - customerAccountId
                      - appUserId
                      - status
                      - dataPackages
                      - authorizedAt
                      - expiresAt
                      - revokedAt
                      - actionRequiredReason
                      - createdAt
                      - updatedAt
                    additionalProperties: false
                    properties: &ref_29
                      id:
                        type: string
                        format: uuid
                      customerAccountId:
                        type: string
                        format: uuid
                      appUserId:
                        type: string
                        maxLength: 256
                      status:
                        type: string
                        enum: &ref_75
                          - PENDING
                          - AUTHORIZED
                          - EXPIRED
                          - REVOKED
                          - ACTION_REQUIRED
                      dataPackages:
                        type: array
                        minItems: 1
                        items:
                          type: string
                          enum: *ref_27
                      authorizedAt:
                        type:
                          - string
                          - 'null'
                        format: date-time
                      expiresAt:
                        type:
                          - string
                          - 'null'
                        format: date-time
                      revokedAt:
                        type:
                          - string
                          - 'null'
                        format: date-time
                      actionRequiredReason:
                        type:
                          - string
                          - 'null'
                      createdAt:
                        type: string
                        format: date-time
                      updatedAt:
                        type: string
                        format: date-time
                  nextAction:
                    type: &ref_30
                      - object
                      - 'null'
                    additionalProperties: false
                    required: &ref_31
                      - type
                      - url
                      - expiresAt
                    properties: &ref_32
                      type:
                        type: string
                        enum:
                          - open_webview
                      url:
                        type: string
                        format: uri
                      expiresAt:
                        type: string
                        format: date-time
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/connections/{connectionId}:
    get:
      summary: Get SDK connection status
      description: Returns the current lifecycle state for an app-owned SDK connection. Pending connections self-heal to authorized when their underlying Curae-hosted handoff session has already completed.
      tags:
        - sdk-v1
        - SDK Connections
      security:
        - platformApiKey: []
      parameters:
        - in: path
          name: connectionId
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Current connection status.
          content:
            application/json:
              schema:
                type: object
                required:
                  - connection
                additionalProperties: false
                properties:
                  connection:
                    type: object
                    required: *ref_28
                    additionalProperties: false
                    properties: *ref_29
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/connections/{connectionId}:revoke:
    post:
      summary: Revoke an SDK connection
      description: Monotonically revokes an app-owned SDK connection and terminates connection-authorized access. Internal enforcement artifacts are not exposed on the public API.
      tags:
        - sdk-v1
        - SDK Connections
      security:
        - platformApiKey: []
      parameters:
        - in: path
          name: connectionId
          required: true
          schema:
            type: string
            format: uuid
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Required idempotency key for safe connection-revoke retries.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                reason:
                  type: string
                  minLength: 1
                  maxLength: 64
                  default: app_revoked
      responses:
        '200':
          description: Connection revocation result.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/json:
              schema:
                type: object
                required:
                  - connection
                  - revokedNow
                additionalProperties: false
                properties:
                  connection:
                    type: object
                    required: *ref_28
                    additionalProperties: false
                    properties: *ref_29
                  revokedNow:
                    type: boolean
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/connections/{connectionId}:reconnect:
    post:
      summary: Reconnect an SDK patient connection
      description: Starts a connection lifecycle recovery flow for an expired, revoked, or action-required SDK connection. Active and pending connections are not reconnected.
      tags:
        - sdk-v1
        - SDK Connections
      security:
        - platformApiKey: []
      parameters:
        - in: path
          name: connectionId
          required: true
          schema:
            type: string
            format: uuid
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Required idempotency key for safe connection-reconnect retries.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties: &ref_78
                handoffReturnUrl:
                  type:
                    - string
                    - 'null'
                  format: uri
                  maxLength: 2048
                deviceKeyCnf:
                  type:
                    - object
                    - 'null'
                  additionalProperties: true
                refreshTokenHash:
                  type:
                    - string
                    - 'null'
                  pattern: ^[0-9a-f]{64}$
                sessionTtlSeconds:
                  type: integer
                  minimum: 60
                  maximum: 900
                authorizationTtlSeconds:
                  type: integer
                  minimum: 300
                  maximum: 31536000
                preferSignup:
                  type: boolean
      responses:
        '202':
          description: SDK connection reconnect started.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-Idempotency-Key:
              schema:
                type: string
            Idempotent-Replay:
              schema:
                type: string
                enum:
                  - 'true'
                  - 'false'
          content:
            application/json:
              schema:
                type: object
                required: &ref_79
                  - connection
                  - previousConnectionId
                  - nextAction
                additionalProperties: false
                properties: &ref_80
                  connection:
                    type: object
                    required: *ref_28
                    additionalProperties: false
                    properties: *ref_29
                  previousConnectionId:
                    type:
                      - string
                      - 'null'
                    format: uuid
                  nextAction:
                    type: *ref_30
                    additionalProperties: false
                    required: *ref_31
                    properties: *ref_32
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/curae-connect/initiate:
    post:
      summary: Initiate a Curae Connect entry-flow session (Path A / B / C selection)
      description: "Mints a fresh CuraeConnectSession for the SDK app's (customerAccountId, appUserId) coordinate. Path selection is deterministic from the bound `LimitedAccountAppBinding`: existing-ACTIVE → Path A (`EXISTING_ACTIVE_HANDOFF`, handoff lands in-tx), no-binding + `preferSignup: true` → Path B (`NEW_ACTIVE_SIGNUP_HANDOFF`, requires `completeHandoff`), else → Path C (`SILENT_LIMITED`, reuses or provisions a LIMITED user). The route requires `connection:write` scope and an `Idempotency-Key` (retries replay the original response verbatim). Rate-limited at 10 req / customer-account / appUserId / minute on top of the standard Platform API buckets."
      tags:
        - sdk-v1
        - SDK Curae Connect
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: Idempotency-Key
          required: true
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Required idempotency key for safe initiate retries.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - appUserId
              additionalProperties: false
              properties:
                appUserId:
                  type: string
                  minLength: 1
                  maxLength: 256
                  description: 'Opaque SDK user id. Must be canonical: no leading or trailing whitespace and no control characters.'
                handoffReturnUrl:
                  type:
                    - string
                    - 'null'
                  format: uri
                  maxLength: 2048
                  description: Absolute handoff callback URL. Production accepts https URLs without user-info; localhost http URLs are accepted only outside production for local development.
                deviceKeyCnf:
                  type:
                    - object
                    - 'null'
                  additionalProperties: true
                refreshTokenHash:
                  type:
                    - string
                    - 'null'
                  pattern: ^[0-9a-f]{64}$
                ttlSeconds:
                  type: integer
                  minimum: 60
                  maximum: 900
                preferSignup:
                  type: boolean
      responses:
        '201':
          description: Curae Connect session minted with deterministic path selection.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-Idempotency-Key:
              schema:
                type: string
            Idempotent-Replay:
              schema:
                type: string
                enum:
                  - 'true'
                  - 'false'
          content:
            application/json:
              schema:
                type: object
                required:
                  - session
                  - path
                  - subjectUserId
                  - createdLimitedAccount
                  - handoffToken
                  - handoffTokenExpiresAt
                additionalProperties: false
                properties:
                  session:
                    type: object
                    required: &ref_82
                      - id
                      - tenantId
                      - customerAccountId
                      - appUserId
                      - subjectUserId
                      - state
                      - handoffReturnUrl
                      - deviceKeyCnf
                      - ttlExpiresAt
                      - createdAt
                      - completedAt
                    additionalProperties: false
                    properties: &ref_83
                      id:
                        type: string
                        format: uuid
                      tenantId:
                        type: string
                        format: uuid
                      customerAccountId:
                        type: string
                        format: uuid
                      appUserId:
                        type: string
                        maxLength: 256
                        description: Opaque SDK user id, stored in canonical form.
                      subjectUserId:
                        type:
                          - string
                          - 'null'
                        format: uuid
                      state:
                        type: string
                        enum: &ref_81
                          - INITIATED
                          - AWAITING_HANDOFF
                          - LIMITED_PROVISIONED
                          - RESUMED_ACTIVE
                          - HANDED_OFF_ACTIVE
                          - TRANSFERRED
                      handoffReturnUrl:
                        type:
                          - string
                          - 'null'
                        format: uri
                        maxLength: 2048
                        description: Absolute safe handoff callback URL, when supplied.
                      deviceKeyCnf:
                        type:
                          - object
                          - 'null'
                        additionalProperties: true
                      ttlExpiresAt:
                        type: string
                        format: date-time
                      createdAt:
                        type: string
                        format: date-time
                      completedAt:
                        type:
                          - string
                          - 'null'
                        format: date-time
                  path:
                    type: string
                    enum: &ref_84
                      - EXISTING_ACTIVE_HANDOFF
                      - NEW_ACTIVE_SIGNUP_HANDOFF
                      - SILENT_LIMITED
                  subjectUserId:
                    type:
                      - string
                      - 'null'
                    format: uuid
                  createdLimitedAccount:
                    type: boolean
                  handoffToken:
                    type:
                      - string
                      - 'null'
                  handoffTokenExpiresAt:
                    type:
                      - string
                      - 'null'
                    format: date-time
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/curae-connect/graduation-handoff:
    post:
      summary: Mint a graduation handoff token for an existing LIMITED user
      description: SDK consumers surface a `<CuraeUpgradeCard>` whenever the current user is in the `LIMITED` lifecycle state; a tap on the upgrade CTA proxies to this route via the consumer backend. The platform mints a fresh server-signed `GRADUATION_HANDOFF` JWT that pins the LIMITED `User.id` and returns the Curae-hosted `/graduate?handoff=<token>` webview URL the SDK navigates the user to. The credential-binding ceremony (passkey, magic link, password) runs inside that webview. Idempotency-Key is REJECTED because every call MUST mint a fresh short-lived JWT — replays would return a stale token whose TTL may have elapsed. Rate-limited at 10 req / customer-account / appUserId / minute (same cap as `/initiate`) to close the LIMITED-binding enumeration side channel.
      tags:
        - sdk-v1
        - SDK Curae Connect
      security:
        - platformApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - appUserId
              additionalProperties: false
              properties:
                appUserId:
                  type: string
                  minLength: 1
                  maxLength: 256
                  description: 'Opaque SDK user id. Must be canonical: no leading or trailing whitespace and no control characters.'
                handoffReturnUrl:
                  type:
                    - string
                    - 'null'
                  format: uri
                  maxLength: 2048
                  description: Optional return URL the `/graduate` webview redirects back to after the graduation ceremony succeeds. Production accepts https URLs without user-info; localhost http URLs are accepted only outside production for local development.
                ttlSeconds:
                  type: integer
                  minimum: 60
                  maximum: 900
                  description: Optional handoff-token TTL override, in seconds. Defaults to 5 minutes server-side.
      responses:
        '201':
          description: Graduation handoff token minted with absolute webview URL.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/json:
              schema:
                type: object
                required:
                  - handoffUrl
                  - handoffToken
                  - handoffTokenExpiresAt
                  - subjectUserId
                additionalProperties: false
                properties:
                  handoffUrl:
                    type: string
                    format: uri
                    description: Absolute Curae-domain URL the SDK navigates the user to. Composition is server-driven so a future Curae release (e.g. regional path prefix) does not require an SDK bump.
                  handoffToken:
                    type: string
                    description: Raw server-signed JWT. The SDK does NOT need to inspect this directly under normal flow — the `handoffUrl` already carries it in the query string — but it is returned for diagnostics and for consumers that need to embed the token into a custom webview launcher.
                  handoffTokenExpiresAt:
                    type: string
                    format: date-time
                    description: ISO 8601 expiry of the handoff token.
                  subjectUserId:
                    type: string
                    format: uuid
                    description: The LIMITED `User.id` the handoff token is bound to. Surfaced to the SDK so consumers can correlate the token to the user without re-resolving the binding.
        '409':
          description: User has already graduated; drive sign-in instead.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
        '410':
          description: Binding slot tombstoned; re-onboard the user.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/curae-connect/account-state:
    get:
      summary: Resolve the lifecycle state of an SDK app-user coordinate (read-only)
      description: Read-only probe used by the SDK's `<CuraeAIAccountProvider>` to decide which onboarding / lifecycle UI to render for the current user. Resolves `(customerAccountId, appUserId)` to one of `UNLINKED`, `LIMITED`, `ACTIVE`, or `TOMBSTONED` without minting a `CuraeConnectSession` row, provisioning a LIMITED user, emitting any outbox event, or counting against the per-coordinate `/initiate` rate-limit bucket. Supports RFC 7232 conditional GET — pass the previously-received ETag in `If-None-Match` to receive a `304 Not Modified` when the state has not moved. Designed for high-frequency polling (every page mount, every browser tab visibility-change).
      tags:
        - sdk-v1
        - SDK Curae Connect
      security:
        - platformApiKey: []
      parameters:
        - in: query
          name: appUserId
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 256
          description: 'Opaque SDK user id. Must be canonical: no leading or trailing whitespace and no control characters.'
        - in: header
          name: If-None-Match
          required: false
          schema:
            type: string
          description: Strong ETag list (RFC 7232 §3.2). When any candidate matches the current account-state ETag, the response is `304 Not Modified` with the same `ETag` header and no body.
      responses:
        '200':
          description: Resolved lifecycle state for the supplied coordinate.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            ETag:
              schema:
                type: string
              description: Strong entity tag derived from `lifecycleState` and `subjectUserId`. Echoed on subsequent conditional GETs via `If-None-Match`.
          content:
            application/json:
              schema:
                type: object
                required:
                  - lifecycleState
                  - subjectUserId
                additionalProperties: false
                properties:
                  lifecycleState:
                    type: string
                    enum:
                      - UNLINKED
                      - LIMITED
                      - ACTIVE
                      - TOMBSTONED
                    description: '`UNLINKED` — no binding exists; render onboarding choice. `LIMITED` — binding resolves to a LIMITED user; render upgrade card. `ACTIVE` — binding resolves to an ACTIVE user; full SDK available. `TOMBSTONED` — binding is unusable (slot soft-deleted, or bound user soft-deleted / deactivated); the consumer must re-onboard.'
                  subjectUserId:
                    type:
                      - string
                      - 'null'
                    format: uuid
                    description: The Curae `User.id` the coordinate currently resolves to. `null` for the `UNLINKED` and `TOMBSTONED` states because no usable user reference exists in those cases.
        '304':
          description: State has not changed since the supplied ETag was issued. No body. The `ETag` header is echoed so consumers can use it as the next conditional probe.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            ETag:
              schema:
                type: string
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/catalog/brands:
    get:
      summary: Search the health-system brand catalog (public)
      description: Public, unauthenticated search over the CuraeAI health-system brand catalog — the discovery surface a consumer uses to build an institution picker before any data is linked. The catalog contains only non-PHI metadata (brand name, EHR platform type, FHIR base URL, city/state, and a logo reference), so no API key is required; the authorization boundary is the SDK connection, not catalog discovery. Results are deduplicated by FHIR system identity and CDN-cached at the edge.
      tags:
        - sdk-v1
        - SDK Catalog
      security: []
      parameters:
        - in: query
          name: q
          required: true
          schema:
            type: string
            minLength: 2
          description: Multi-word search query matched against brand name, aliases, city, and state. Queries shorter than 2 characters return an empty result set.
        - in: query
          name: platformType
          required: false
          schema:
            type: string
          description: Optional filter to a single EHR platform (e.g. `EPIC`).
        - in: query
          name: limit
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
          description: Maximum number of brands to return (1..100, default 25).
      responses:
        '200':
          description: Matching catalog brands.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/json:
              schema:
                type: object
                required:
                  - brands
                  - count
                additionalProperties: false
                properties:
                  brands:
                    type: array
                    items:
                      type: object
                      description: A health-system brand in the public CuraeAI Connect catalog. Contains only non-PHI discovery metadata used to build an institution picker.
                      required: &ref_33
                        - id
                        - name
                        - platformType
                        - fhirBaseUrl
                        - city
                        - state
                        - portalUrl
                        - logoUrl
                      additionalProperties: false
                      properties: &ref_34
                        id:
                          type: string
                          description: Opaque, URL-safe brand identifier. Stable for a given (platformType, FHIR base URL) pair; pass to the brand and logo endpoints. Treat as opaque — the encoding is not part of the contract.
                        name:
                          type: string
                          description: Patient-facing health-system name.
                        platformType:
                          type: string
                          description: EHR platform type (e.g. `EPIC`, `CERNER`, `ECLINICALWORKS`).
                        fhirBaseUrl:
                          type: string
                          format: uri
                          description: FHIR R4 base URL a connection to this brand authorizes against.
                        city:
                          type:
                            - string
                            - 'null'
                        state:
                          type:
                            - string
                            - 'null'
                          description: US state abbreviation, when known.
                        portalUrl:
                          type:
                            - string
                            - 'null'
                          format: uri
                          description: Public portal / website URL, when known.
                        logoUrl:
                          type: string
                          description: Relative URL to the brand-logo endpoint (`/api/sdk/v1/catalog/brands/{id}/logo`), which 307-redirects to the backing artwork or a favicon fallback.
                  count:
                    type: integer
                    minimum: 0
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/catalog/brands/{brandId}:
    get:
      summary: Get a single catalog brand by id (public)
      description: Public, unauthenticated lookup of one catalog brand by its opaque `brandId` (as returned by the catalog search). Non-PHI metadata; CDN-cached. An unknown or malformed id returns 404 problem details.
      tags:
        - sdk-v1
        - SDK Catalog
      security: []
      parameters:
        - in: path
          name: brandId
          required: true
          schema:
            type: string
          description: Opaque, URL-safe brand identifier from the catalog search.
      responses:
        '200':
          description: The resolved catalog brand.
          headers:
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
          content:
            application/json:
              schema:
                type: object
                required:
                  - brand
                additionalProperties: false
                properties:
                  brand:
                    type: object
                    description: A health-system brand in the public CuraeAI Connect catalog. Contains only non-PHI discovery metadata used to build an institution picker.
                    required: *ref_33
                    additionalProperties: false
                    properties: *ref_34
        '404':
          description: No catalog brand matches the supplied id.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/sdk/v1/catalog/brands/{brandId}/logo:
    get:
      summary: Get a catalog brand's logo (public)
      description: Public, unauthenticated brand-logo endpoint. Issues a 307 redirect to the backing artwork (a pre-rendered asset or a favicon fallback) so callers can use one stable Curae URL — typically as an `<img>` src — while the platform retains freedom to change the underlying source. Returns 404 when no artwork is resolvable. Long-lived CDN cache.
      tags:
        - sdk-v1
        - SDK Catalog
      security: []
      parameters:
        - in: path
          name: brandId
          required: true
          schema:
            type: string
          description: Opaque, URL-safe brand identifier from the catalog search.
      responses:
        '307':
          description: Redirect to the brand logo asset.
          headers:
            Location:
              schema:
                type: string
                format: uri
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
        '404':
          description: No logo is available for the supplied brand id.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
        default:
          description: RFC 7807 problem details.
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/platform/v1/webhook-endpoints:
    get:
      summary: List Platform webhook endpoints
      description: Lists non-deleted webhook endpoints owned by the API key's customer account, sorted by `createdAt DESC, id DESC` (newest first), with forward-only cursor pagination as specified in `PLATFORM_API_ARCHITECTURE.md §10`.
      tags:
        - platform-v1
        - Platform Webhooks
      security:
        - platformApiKey: []
      parameters:
        - in: query
          name: limit
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 25
          description: Page size (1..100). Defaults to 25 when omitted. Requests with values outside this range receive `400 BAD_REQUEST` rather than being silently clamped, so a misconfigured client surfaces the bug immediately.
        - in: query
          name: cursor
          required: false
          schema:
            type: string
          description: Opaque pagination cursor previously returned in `pagination.nextCursor`. Clients MUST treat the value as a black box and pass it through unchanged. Malformed or cross-endpoint cursors return `400 BAD_REQUEST`.
      responses:
        '200':
          description: Webhook endpoint list
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
          content:
            application/json:
              schema:
                type: object
                required: &ref_88
                  - data
                  - pagination
                  - _links
                properties: &ref_89
                  data:
                    type: array
                    items:
                      type: object
                      required: &ref_41
                        - id
                        - resourceType
                        - url
                        - eventTypes
                        - status
                        - consecutiveFailures
                        - lastDeliveryAttemptAt
                        - lastSuccessfulDeliveryAt
                        - createdAt
                        - updatedAt
                        - _version
                        - _links
                      properties: &ref_42
                        id:
                          type: string
                          format: uuid
                        resourceType:
                          type: string
                          const: WebhookEndpoint
                        url:
                          type: string
                          format: uri
                        eventTypes:
                          type: array
                          minItems: 1
                          items:
                            type: string
                            enum: &ref_40
                              - patient.created
                              - patient.updated
                              - observation.created
                              - observation.updated
                              - import.completed
                              - connection.authorized
                              - connection.revoked
                              - connection.expired
                              - connection.action_required
                              - connection.reconnected
                              - records.export_queued
                              - records.export_ready
                              - records.export_failed
                              - records.export_expired
                              - entry.initiated
                              - entry.completed
                              - entry.limited_provisioned
                              - entry.transferred
                              - entry.folded
                              - user.graduated
                              - consolidation.completed
                              - consolidation.reversed
                        status:
                          type: string
                          enum: &ref_85
                            - ACTIVE
                            - DISABLED
                            - SUSPENDED
                        consecutiveFailures:
                          type: integer
                          minimum: 0
                        lastDeliveryAttemptAt:
                          type: string
                          format: date-time
                          nullable: true
                        lastSuccessfulDeliveryAt:
                          type: string
                          format: date-time
                          nullable: true
                        createdAt:
                          type: string
                          format: date-time
                        updatedAt:
                          type: string
                          format: date-time
                        _version:
                          type: string
                          description: |
                            Strong ETag-compatible version that changes only when the customer-visible
                            configuration (`url`, `eventTypes`, `status`) changes. Operational fields
                            (`consecutiveFailures`, `lastDeliveryAttemptAt`, `lastSuccessfulDeliveryAt`)
                            mutate on every outbound delivery and do NOT affect `_version`, so
                            conditional `GET` requests return `304 Not Modified` for unchanged
                            configuration even on busy endpoints.
                        _links:
                          type: object
                          required: *ref_35
                          properties: *ref_36
                  pagination:
                    type: object
                    description: 'Cursor-based pagination envelope returned alongside the `data` array on every Platform API list response. The pagination contract is documented in `PLATFORM_API_ARCHITECTURE.md §10`: cursors are opaque, forward-only, and may change shape across versions without notice. `nextCursor` is `null` exactly when `hasMore` is `false`, so a client can stop iterating without parsing the cursor.'
                    required: &ref_86
                      - nextCursor
                      - hasMore
                    properties: &ref_87
                      nextCursor:
                        type: string
                        nullable: true
                        description: Opaque cursor to pass as `?cursor=` on the next page request, or `null` when there are no further pages. Treat as a black box — base64url payload is not part of the public contract.
                      hasMore:
                        type: boolean
                        description: '`true` when the server has at least one more page of results beyond the current `data` array. Mirrors `nextCursor !== null`.'
                  _links:
                    type: object
                    required:
                      - self
                    properties:
                      self:
                        type: object
                        required: *ref_37
                        properties: *ref_38
        default:
          description: RFC 7807 problem details
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            Retry-After:
              description: Seconds to wait before retrying a rate-limited request.
              schema: *ref_39
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
    post:
      summary: Create a Platform webhook endpoint
      description: Creates a webhook endpoint for the API key's customer account. The signing secret is returned only in this response.
      tags:
        - platform-v1
        - Platform Webhooks
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: Idempotency-Key
          required: false
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Optional idempotency key to make this mutation safe to retry. Replays of the same `(apiKey, key)` within 24h return the original response.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: &ref_90
                - url
                - eventTypes
              additionalProperties: false
              properties: &ref_91
                url:
                  type: string
                  format: uri
                eventTypes:
                  type: array
                  minItems: 1
                  items:
                    oneOf:
                      - type: string
                        enum: *ref_40
                      - type: string
                        enum:
                          - consent.*
                          - patient.*
                          - observation.*
                          - entry.*
                          - consolidation.*
      responses:
        '201':
          description: Created webhook endpoint
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            ETag:
              description: Strong entity tag matching the response `_version`.
              schema:
                type: string
            Location:
              description: Path of the created webhook endpoint resource.
              schema:
                type: string
          content:
            application/json:
              schema:
                allOf: &ref_92
                  - type: object
                    required: *ref_41
                    properties: *ref_42
                  - type: object
                    required:
                      - signingSecret
                    properties:
                      signingSecret:
                        type: string
                        description: Plaintext signing secret returned only once at endpoint creation.
        default:
          description: RFC 7807 problem details
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            Retry-After:
              description: Seconds to wait before retrying a rate-limited request.
              schema: *ref_39
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
  /api/platform/v1/webhook-endpoints/{id}:
    parameters:
      - in: path
        name: id
        required: true
        schema:
          type: string
          format: uuid
        description: Webhook endpoint identifier.
    get:
      summary: Retrieve a Platform webhook endpoint
      description: Retrieves a webhook endpoint owned by the API key's customer account.
      tags:
        - platform-v1
        - Platform Webhooks
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: If-None-Match
          required: false
          schema:
            type: string
          description: Optional weak or strong entity tag. Matching validators return `304 Not Modified`.
      responses:
        '200':
          description: Webhook endpoint
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            ETag:
              description: Strong entity tag matching the response `_version`.
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                required: *ref_41
                properties: *ref_42
        '304':
          description: The webhook endpoint has not changed since the supplied If-None-Match validator.
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            ETag:
              description: Current strong entity tag for the webhook endpoint resource.
              schema:
                type: string
        default:
          description: RFC 7807 problem details
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            Retry-After:
              description: Seconds to wait before retrying a rate-limited request.
              schema: *ref_39
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
    patch:
      summary: Update a Platform webhook endpoint
      description: Updates mutable fields of a webhook endpoint owned by the API key's customer account.
      tags:
        - platform-v1
        - Platform Webhooks
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: If-Match
          required: false
          schema:
            type: string
          description: Optional entity tag precondition. When supplied, the mutation succeeds only if the current resource version matches one of the validators or `*`.
        - in: header
          name: Idempotency-Key
          required: false
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Optional idempotency key to make this mutation safe to retry. Replays of the same `(apiKey, key)` within 24h return the original response.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              minProperties: 1
              properties: &ref_93
                url:
                  type: string
                  format: uri
                eventTypes:
                  type: array
                  minItems: 1
                  items:
                    oneOf:
                      - type: string
                        enum: *ref_40
                      - type: string
                        enum:
                          - consent.*
                          - patient.*
                          - observation.*
                          - entry.*
                          - consolidation.*
                status:
                  type: string
                  enum:
                    - ACTIVE
                    - DISABLED
      responses:
        '200':
          description: Updated webhook endpoint
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            ETag:
              description: Strong entity tag matching the response `_version`.
              schema:
                type: string
          content:
            application/json:
              schema:
                type: object
                required: *ref_41
                properties: *ref_42
        default:
          description: RFC 7807 problem details
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            Retry-After:
              description: Seconds to wait before retrying a rate-limited request.
              schema: *ref_39
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
    delete:
      summary: Delete a Platform webhook endpoint
      description: Soft-deletes a webhook endpoint owned by the API key's customer account.
      tags:
        - platform-v1
        - Platform Webhooks
      security:
        - platformApiKey: []
      parameters:
        - in: header
          name: If-Match
          required: false
          schema:
            type: string
          description: Optional entity tag precondition. When supplied, the delete succeeds only if the current resource version matches one of the validators or `*`.
        - in: header
          name: Idempotency-Key
          required: false
          schema:
            type: string
            maxLength: 255
            pattern: ^[A-Za-z0-9_-]+$
          description: Optional idempotency key to make this mutation safe to retry. Replays of the same `(apiKey, key)` within 24h return the original response.
      responses:
        '204':
          description: Webhook endpoint deleted. No response body is returned.
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
        default:
          description: RFC 7807 problem details
          headers:
            CuraeAI-Version:
              description: Effective Platform API dated version used for this response.
              schema: *ref_0
            Cache-Control:
              description: Cache policy applied to the response.
              schema: *ref_1
            X-RateLimit-Limit:
              description: Request limit for the rate-limit bucket that determined the response metadata.
              schema: *ref_2
            X-RateLimit-Remaining:
              description: Requests remaining in the current rate-limit window.
              schema: *ref_3
            X-RateLimit-Reset:
              description: Unix timestamp in seconds when the current rate-limit window resets.
              schema: *ref_4
            X-RateLimit-Window:
              description: Rate-limit window length in seconds.
              schema: *ref_5
            Retry-After:
              description: Seconds to wait before retrying a rate-limited request.
              schema: *ref_39
          content:
            application/problem+json:
              schema:
                type: object
                required: *ref_13
                properties: *ref_14
components:
  headers:
    PlatformApiVersion:
      description: Effective Platform API dated version used for this response.
      schema: *ref_0
    PlatformCacheControl:
      description: Cache policy applied to the response.
      schema: *ref_1
    PlatformRateLimitLimit:
      description: Request limit for the rate-limit bucket that determined the response metadata.
      schema: *ref_2
    PlatformRateLimitRemaining:
      description: Requests remaining in the current rate-limit window.
      schema: *ref_3
    PlatformRateLimitReset:
      description: Unix timestamp in seconds when the current rate-limit window resets.
      schema: *ref_4
    PlatformRateLimitWindow:
      description: Rate-limit window length in seconds.
      schema: *ref_5
    PlatformRetryAfter:
      description: Seconds to wait before retrying a rate-limited request.
      schema: *ref_39
  schemas:
    PlatformProblemDetails:
      type: object
      required: *ref_13
      properties: *ref_14
    PlatformResourceLink:
      type: object
      required: *ref_37
      properties: *ref_38
    PlatformResourceLinks:
      type: object
      required: *ref_35
      properties: *ref_36
    PlatformPatient:
      type: object
      required: *ref_43
      properties: *ref_44
    SdkFhirCoding:
      type: object
      required: *ref_6
      additionalProperties: false
      properties: *ref_7
    SdkFhirObservationMeta:
      type: object
      additionalProperties: true
      description: 'FHIR `Meta`. The platform stamps `source` (a tenant-anchored URN) and `tag` (system + code + display for the SDK source kind). `includeRawFhir: true` requests may carry additional passthrough fields from the source FHIR resource — hence `additionalProperties: true`. The base contract guarantees `source` and `tag`.'
      properties: *ref_45
    SdkFhirCodeableConcept:
      type: object
      additionalProperties: false
      properties: *ref_8
    SdkFhirQuantity:
      type: object
      additionalProperties: false
      properties: *ref_9
    SdkFhirReferenceRange:
      type: object
      additionalProperties: false
      properties: *ref_10
    SdkFhirObservationComponent:
      type: object
      required: *ref_46
      additionalProperties: false
      properties: *ref_47
    SdkFhirObservation:
      type: object
      required: *ref_48
      additionalProperties: true
      description: 'Curae-shaped FHIR R4 Observation projection. Fields below are the contract; `additionalProperties: true` allows passthrough fields when the consumer requests `includeRawFhir: true`.'
      properties: *ref_49
    SdkFhirBundleEntry:
      type: object
      required: *ref_50
      additionalProperties: false
      properties: *ref_51
    SdkObservationSearchBundle:
      type: object
      required: *ref_11
      additionalProperties: false
      description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Observation projections matched by the request scope.
      properties: *ref_12
    SdkFhirConditionMeta:
      type: object
      additionalProperties: true
      description: FHIR `Meta` for Condition projections. Same shape as `SdkFhirObservationMeta`; defined separately to keep the per-resource OpenAPI surfaces independently evolvable.
      properties: *ref_52
    SdkFhirCondition:
      type: object
      required: *ref_53
      additionalProperties: true
      description: 'Curae-shaped FHIR R4 Condition projection. Fields below are the contract; `additionalProperties: true` allows passthrough fields when the consumer requests `includeRawFhir: true`. See `lib/sdk/condition-search.service.ts#buildConditionResource` for the canonical projection.'
      properties: *ref_54
    SdkFhirConditionBundleEntry:
      type: object
      required: *ref_55
      additionalProperties: false
      properties: *ref_56
    SdkConditionSearchBundle:
      type: object
      required: *ref_15
      additionalProperties: false
      description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Condition projections matched by the request scope.
      properties: *ref_16
    SdkFhirImmunizationMeta:
      type: object
      additionalProperties: true
      description: FHIR `Meta` for Immunization projections.
      properties: *ref_57
    SdkFhirImmunization:
      type: object
      required: *ref_58
      additionalProperties: true
      description: 'Curae-shaped FHIR R4 Immunization projection. Fields below are the contract; `additionalProperties: true` allows passthrough fields when the consumer requests `includeRawFhir: true`. See `lib/sdk/immunization-search.service.ts#buildImmunizationResource` for the canonical projection.'
      properties: *ref_59
    SdkFhirImmunizationBundleEntry:
      type: object
      required: *ref_60
      additionalProperties: false
      properties: *ref_61
    SdkImmunizationSearchBundle:
      type: object
      required: *ref_17
      additionalProperties: false
      description: Top-level FHIR R4 Bundle of `searchset` type carrying the Curae-shaped Immunization projections matched by the request scope.
      properties: *ref_18
    SdkSignalType:
      type: object
      additionalProperties: false
      required: *ref_19
      description: |-
        Structured `(namespace, code, version)` tuple identifying an SDK-derived signal. Replaces the pre-W1 flat `AppDerivedSignalType` enum (`Docs/strategy/SDK_PRODUCT_FINDINGS.md` § 3 W1) so adding a new clinical / wellness domain is a platform-side registry addition rather than a Prisma enum migration.
        Structurally a 1:1 projection of FHIR R4's `Coding` shape with simpler field names — the FHIR-canonical projection is `{system: 'https://curaeai.com/fhir/CodeSystem/sdk-signal/<namespace>', code: <code>, version: <version>}`. The simpler tuple is the developer-facing wire shape ("make it as easy as possible to develop medical apps", per the platform thesis); downstream FHIR servers and bulk exports project to the canonical form.
        Consumer-app attribution is **not** part of the type — the emitting customer-account is captured by the API key's principal context and surfaced through audit logs and webhook payloads. Two different sexual-health apps emitting the same `{namespace: 'sti', code: 'exposure_event', version: 'v1'}` are emitting the same semantic signal and resolve to the same platform sensitivity classification.
        Signals are for app-derived **events** with no EHR analog (exposure-notification dispatches, behavioural check-ins, self-asserted lifestyle facts). EHR-derivable questions ("does the user have a negative STI panel within 30 days?", "is the user up-to-date on CDC childhood vaccinations?") are **queries against the resource-search surface**, not signals — see `searchSdkObservations` / `searchSdkConditions` / `searchSdkImmunizations`.
      properties: *ref_20
    SdkRecordExportResourceType:
      type: string
      enum: *ref_21
    SdkRecordExportFormat:
      type: string
      enum: *ref_22
    SdkRecordExportCreateRequest:
      type: object
      required: *ref_62
      additionalProperties: false
      properties: *ref_63
    SdkRecordExportStatus:
      type: string
      enum: *ref_64
    SdkRecordExportArtifact:
      type: *ref_65
      required: *ref_66
      additionalProperties: false
      properties: *ref_67
    SdkRecordExport:
      type: object
      required: *ref_25
      additionalProperties: false
      properties: *ref_26
    SdkRecordExportResponse:
      type: object
      required: *ref_23
      additionalProperties: false
      properties: *ref_24
    SdkRecordExportDownload:
      type: object
      description: Short-lived signed URL for a materialized records-export artifact. The URL is valid for `expiresInSeconds` and is the only way to retrieve the private artifact; redeem it promptly. The artifact's integrity hash and size are available on the accompanying `export` object.
      required: *ref_68
      additionalProperties: false
      properties: *ref_69
    SdkRecordExportDownloadResponse:
      type: object
      required: *ref_70
      additionalProperties: false
      properties: *ref_71
    SdkConnectionDataPackage:
      type: string
      enum: *ref_27
    SdkConnectionFilters:
      type: object
      additionalProperties: false
      properties: *ref_72
    SdkConnectionCreateRequest:
      type: object
      required: *ref_73
      additionalProperties: false
      properties: *ref_74
    SdkConnectionStatus:
      type: string
      enum: *ref_75
    SdkConnection:
      type: object
      required: *ref_28
      additionalProperties: false
      properties: *ref_29
    SdkConnectionNextAction:
      type: *ref_30
      additionalProperties: false
      required: *ref_31
      properties: *ref_32
    SdkConnectionCreateResponse:
      type: object
      required: *ref_76
      additionalProperties: false
      properties: *ref_77
    SdkConnectionReconnectRequest:
      type: object
      additionalProperties: false
      properties: *ref_78
    SdkConnectionReconnectResponse:
      type: object
      required: *ref_79
      additionalProperties: false
      properties: *ref_80
    SdkCuraeConnectSessionState:
      type: string
      enum: *ref_81
    SdkCuraeConnectSession:
      type: object
      required: *ref_82
      additionalProperties: false
      properties: *ref_83
    SdkCuraeConnectInitiatePath:
      type: string
      enum: *ref_84
    SdkCatalogBrand:
      type: object
      description: A health-system brand in the public CuraeAI Connect catalog. Contains only non-PHI discovery metadata used to build an institution picker.
      required: *ref_33
      additionalProperties: false
      properties: *ref_34
    PlatformWebhookEventType:
      type: string
      enum: *ref_40
    PlatformWebhookEndpointStatus:
      type: string
      enum: *ref_85
    PlatformWebhookEndpoint:
      type: object
      required: *ref_41
      properties: *ref_42
    PlatformPagination:
      type: object
      description: 'Cursor-based pagination envelope returned alongside the `data` array on every Platform API list response. The pagination contract is documented in `PLATFORM_API_ARCHITECTURE.md §10`: cursors are opaque, forward-only, and may change shape across versions without notice. `nextCursor` is `null` exactly when `hasMore` is `false`, so a client can stop iterating without parsing the cursor.'
      required: *ref_86
      properties: *ref_87
    PlatformWebhookEndpointListResponse:
      type: object
      required: *ref_88
      properties: *ref_89
    PlatformWebhookEndpointCreateRequest:
      type: object
      required: *ref_90
      additionalProperties: false
      properties: *ref_91
    PlatformWebhookEndpointCreateResponse:
      allOf: *ref_92
    PlatformWebhookEndpointUpdateRequest:
      type: object
      additionalProperties: false
      minProperties: 1
      properties: *ref_93
  securitySchemes:
    platformApiKey:
      type: http
      scheme: bearer
      description: CuraeAI Platform API key using the format **Bearer <api-key>**. Keys are opaque credentials such as `cae_live_...`, not JWTs.
