openapi: 3.1.0
info:
  title: MedTracker API
  version: v1
  description: Hosted MedTracker API contract for mobile clients, API clients, and portable migration.
servers:
  - url: /api/v1
security:
  - bearerAuth: []
tags:
  - name: Public
    description: Unauthenticated capability discovery and session-establishment operations.
  - name: Account
    description: Authenticated account and session operations outside a selected household.
  - name: Household
    description: Ordinary operations within a selected household, including portability and synchronization.
  - name: Household administration
    description: Administrative operations scoped to a selected household.
  - name: Capabilities
    description: API capability discovery.
  - name: Authentication
    description: Login, session, and credential lifecycle operations.
  - name: People
    description: Household people and current-profile operations.
  - name: Locations
    description: Household location records.
  - name: Medications
    description: Medication records and inventory/reorder actions.
  - name: Dosage options
    description: Medication dosage-option records.
  - name: Health events
    description: Health-event records.
  - name: Schedules
    description: Medication schedules and lifecycle actions.
  - name: Person medications
    description: Medication assignments to people.
  - name: Medication takes
    description: Medication administration records.
  - name: Notification preferences
    description: Signed-in notification preferences.
  - name: Devices
    description: Native device tokens and web push subscriptions.
  - name: Medication lookup
    description: External medication lookup operations.
  - name: Medication suggestions
    description: AI-assisted medication setup suggestions.
  - name: Portability
    description: Portable household import/export and data-export operations.
  - name: Synchronization
    description: Mobile snapshots and incremental synchronization feeds/batches.
  - name: Household settings
    description: Household administration settings.
  - name: Memberships
    description: Household membership administration.
  - name: Invitations
    description: Household invitation administration.
  - name: Access grants
    description: Person access-grant administration.
  - name: App tokens
    description: Household API app-token administration.
  - name: Audit logs
    description: Household security audit events.
paths:
  /capabilities:
    get:
      operationId: getCapabilities
      tags:
        - Public
        - Capabilities
      summary: Describe supported API features, diagnostics, and first-party client-tool contracts.
      security: []
      responses:
        '200':
          description: API capability document, including CLI and MCP availability.
  /auth/login:
    post:
      operationId: createLoginSession
      tags:
        - Public
        - Authentication
      summary: Exchange development or migration password credentials for API tokens.
      security: []
      responses:
        '201':
          description: API session created.
        '401':
          description: Credentials are invalid or password login is unavailable.
  /auth/refresh:
    post:
      operationId: refreshSession
      tags:
        - Account
        - Authentication
      summary: Rotate an API session refresh token.
      security: []
      responses:
        '200':
          description: New access and refresh tokens.
        '401':
          description: Refresh token is invalid or expired.
  /auth/oidc_exchange:
    post:
      operationId: exchangeOidcSession
      tags:
        - Public
        - Authentication
      summary: Exchange a hosted OIDC ID token for an API session.
      security: []
      responses:
        '201':
          description: API session created.
  /auth/households:
    get:
      operationId: listHouseholds
      tags:
        - Account
        - Authentication
      summary: List households available to the current API credential.
      responses:
        '200':
          description: Household collection.
  /auth/sessions:
    get:
      operationId: listSessions
      tags:
        - Account
        - Authentication
      summary: List active API sessions for the current account.
      responses:
        '200':
          description: API session collection.
  /auth/sessions/{id}:
    delete:
      operationId: deleteSession
      tags:
        - Account
        - Authentication
      summary: Revoke an API session.
      parameters:
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: API session revoked.
  /auth/logout:
    delete:
      operationId: logoutSession
      tags:
        - Account
        - Authentication
      summary: Revoke the current API session or app token.
      responses:
        '204':
          description: Credential revoked.
  /households/{household_id}/me:
    get:
      operationId: getCurrentProfile
      tags:
        - Household
        - People
      summary: Read the current account and person profile for a household session.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Current user profile.
  /households/{household_id}/people:
    get:
      operationId: listPeople
      tags:
        - Household
        - People
      summary: List visible people in the household.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/page'
        - $ref: '#/components/parameters/per_page'
        - $ref: '#/components/parameters/updated_since'
      responses:
        '200':
          description: People collection.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersonCollectionResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      operationId: createPerson
      tags:
        - Household
        - People
      summary: Create a person in the household.
      parameters:
        - $ref: '#/components/parameters/household_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PersonCreateRequest'
      responses:
        '201':
          description: Person created.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersonResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/RateLimited'
  /households/{household_id}/people/{id}:
    get:
      operationId: getPerson
      tags:
        - Household
        - People
      summary: Read a person.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: Person resource.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersonResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
    patch:
      operationId: updatePerson
      tags:
        - Household
        - People
      summary: Update a person.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PersonUpdateRequest'
      responses:
        '200':
          description: Person updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersonResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/RateLimited'
    put:
      operationId: updatePersonWithPut
      tags:
        - Household
        - People
      summary: Update a person using the PUT route.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PersonUpdateRequest'
      responses:
        '200':
          description: Person updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PersonResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationFailed'
        '429':
          $ref: '#/components/responses/RateLimited'
  /households/{household_id}/locations:
    get:
      operationId: listLocations
      tags:
        - Household
        - Locations
      summary: List visible locations.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Locations collection.
  /households/{household_id}/locations/{id}:
    get:
      operationId: getLocation
      tags:
        - Household
        - Locations
      summary: Read a location.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Location resource.
  /households/{household_id}/medications:
    get:
      operationId: listMedications
      tags:
        - Household
        - Medications
      summary: List visible medications.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Medications collection.
    post:
      operationId: createMedication
      tags:
        - Household
        - Medications
      summary: Create a medication.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Medication created.
  /households/{household_id}/medications/{id}:
    get:
      operationId: getMedication
      tags:
        - Household
        - Medications
      summary: Read a medication.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Medication resource.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
    patch:
      operationId: updateMedication
      tags:
        - Household
        - Medications
      summary: Update a medication.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Medication updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
    put:
      operationId: replaceMedication
      tags:
        - Household
        - Medications
      summary: Replace a medication.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Medication updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
  /households/{household_id}/medications/{id}/adjust_inventory:
    patch:
      operationId: adjustMedicationInventory
      tags:
        - Household
        - Medications
      summary: Adjust medication inventory.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Medication inventory adjusted.
  /households/{household_id}/medications/{id}/mark_as_ordered:
    patch:
      operationId: markMedicationAsOrdered
      tags:
        - Household
        - Medications
      summary: Mark a medication reorder as ordered.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Reorder status updated.
  /households/{household_id}/medications/{id}/mark_as_received:
    patch:
      operationId: markMedicationAsReceived
      tags:
        - Household
        - Medications
      summary: Mark a medication reorder as received.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Reorder status updated.
  /households/{household_id}/dosage_options:
    get:
      operationId: listDosageOptions
      tags:
        - Household
        - Dosage options
      summary: List medication dosage options.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Dosage option collection.
    post:
      operationId: createDosageOption
      tags:
        - Household
        - Dosage options
      summary: Create a medication dosage option.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Dosage option created.
  /households/{household_id}/dosage_options/{id}:
    get:
      operationId: getDosageOption
      tags:
        - Household
        - Dosage options
      summary: Read a medication dosage option.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Dosage option resource.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
    patch:
      operationId: updateDosageOption
      tags:
        - Household
        - Dosage options
      summary: Update a medication dosage option.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Dosage option updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
    put:
      operationId: replaceDosageOption
      tags:
        - Household
        - Dosage options
      summary: Replace a medication dosage option.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Dosage option updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
  /households/{household_id}/health_events:
    get:
      operationId: listHealthEvents
      tags:
        - Household
        - Health events
      summary: List visible health events.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Health event collection.
    post:
      operationId: createHealthEvent
      tags:
        - Household
        - Health events
      summary: Create a health event.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Health event created.
  /households/{household_id}/health_events/{id}:
    get:
      operationId: getHealthEvent
      tags:
        - Household
        - Health events
      summary: Read a health event.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Health event resource.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
    patch:
      operationId: updateHealthEvent
      tags:
        - Household
        - Health events
      summary: Update a health event.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Health event updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
    put:
      operationId: replaceHealthEvent
      tags:
        - Household
        - Health events
      summary: Replace a health event.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Health event updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
  /households/{household_id}/schedules:
    get:
      operationId: listSchedules
      tags:
        - Household
        - Schedules
      summary: List visible schedules.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Schedules collection.
    post:
      operationId: createSchedule
      tags:
        - Household
        - Schedules
      summary: Create a schedule.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Schedule created.
  /households/{household_id}/schedules/{id}:
    get:
      operationId: getSchedule
      tags:
        - Household
        - Schedules
      summary: Read a schedule.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Schedule resource.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
    patch:
      operationId: updateSchedule
      tags:
        - Household
        - Schedules
      summary: Update a schedule.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Schedule updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
    put:
      operationId: replaceSchedule
      tags:
        - Household
        - Schedules
      summary: Replace a schedule.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Schedule updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
  /households/{household_id}/schedules/{id}/pause:
    patch:
      operationId: pauseSchedule
      tags:
        - Household
        - Schedules
      summary: Pause a schedule.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Schedule paused.
  /households/{household_id}/schedules/{id}/resume:
    patch:
      operationId: resumeSchedule
      tags:
        - Household
        - Schedules
      summary: Resume a schedule.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Schedule resumed.
  /households/{household_id}/person_medications:
    get:
      operationId: listPersonMedications
      tags:
        - Household
        - Person medications
      summary: List visible person medication assignments.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Person medication collection.
    post:
      operationId: createPersonMedication
      tags:
        - Household
        - Person medications
      summary: Create a person medication assignment.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Person medication created.
  /households/{household_id}/person_medications/{id}:
    get:
      operationId: getPersonMedication
      tags:
        - Household
        - Person medications
      summary: Read a person medication assignment.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Person medication resource.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
    patch:
      operationId: updatePersonMedication
      tags:
        - Household
        - Person medications
      summary: Update a person medication assignment.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Person medication updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
    put:
      operationId: replacePersonMedication
      tags:
        - Household
        - Person medications
      summary: Replace a person medication assignment.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
        - $ref: '#/components/parameters/if_match'
      responses:
        '200':
          description: Person medication updated.
          headers:
            ETag:
              $ref: '#/components/headers/etag'
        '409':
          $ref: '#/components/responses/Conflict'
  /households/{household_id}/person_medications/{id}/pause:
    patch:
      operationId: pausePersonMedication
      tags:
        - Household
        - Person medications
      summary: Pause a person medication assignment.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Person medication paused.
  /households/{household_id}/person_medications/{id}/resume:
    patch:
      operationId: resumePersonMedication
      tags:
        - Household
        - Person medications
      summary: Resume a person medication assignment.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Person medication resumed.
  /households/{household_id}/person_medications/{id}/reorder:
    patch:
      operationId: reorderPersonMedication
      tags:
        - Household
        - Person medications
      summary: Reorder a person medication assignment.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/resource_id'
      responses:
        '200':
          description: Person medication reordered.
  /households/{household_id}/medication_takes:
    get:
      operationId: listMedicationTakes
      tags:
        - Household
        - Medication takes
      summary: List visible medication takes.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Medication takes collection.
    post:
      operationId: createMedicationTake
      tags:
        - Household
        - Medication takes
      summary: Record a medication take.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Medication take created.
  /households/{household_id}/notification_preference:
    get:
      operationId: getNotificationPreference
      tags:
        - Household
        - Notification preferences
      summary: Read the signed-in person's notification preference.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Notification preference resource.
    patch:
      operationId: updateNotificationPreference
      tags:
        - Household
        - Notification preferences
      summary: Update the signed-in person's notification preference.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Notification preference updated.
    put:
      operationId: replaceNotificationPreference
      tags:
        - Household
        - Notification preferences
      summary: Replace the signed-in person's notification preference.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Notification preference updated.
  /households/{household_id}/native_device_tokens:
    post:
      operationId: createNativeDeviceToken
      tags:
        - Household
        - Devices
      summary: Register a native device token.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Device token registered.
  /households/{household_id}/native_device_tokens/{id}:
    delete:
      operationId: deleteNativeDeviceToken
      tags:
        - Household
        - Devices
      summary: Revoke a native device token.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/native_device_token'
      responses:
        '204':
          description: Device token revoked.
  /households/{household_id}/push_subscription:
    post:
      operationId: createPushSubscription
      tags:
        - Household
        - Devices
      summary: Register a web push subscription.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Push subscription registered.
    delete:
      operationId: deletePushSubscription
      tags:
        - Household
        - Devices
      summary: Revoke a web push subscription.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '204':
          description: Push subscription revoked.
  /households/{household_id}/push_subscription/test:
    post:
      operationId: testPushSubscription
      tags:
        - Household
        - Devices
      summary: Send a test push notification.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '204':
          description: Test notification queued.
  /households/{household_id}/medication_lookup:
    get:
      operationId: searchMedicationLookup
      tags:
        - Household
        - Medication lookup
      summary: Search external medication lookup data.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Medication lookup results.
  /households/{household_id}/ai_medication_suggestions:
    post:
      operationId: generateAiMedicationSuggestions
      tags:
        - Household
        - Medication suggestions
      summary: Generate medication setup suggestions.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Suggestion payload.
  /households/{household_id}/portable_export:
    get:
      operationId: exportPortableHouseholdBundle
      tags:
        - Household
        - Portability
      summary: Export an encrypted portable household bundle.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Encrypted portable bundle.
  /households/{household_id}/mobile_snapshot:
    get:
      operationId: getMobileSnapshot
      tags:
        - Household
        - Synchronization
      summary: Read a plaintext mobile sync snapshot over authenticated transport.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Mobile snapshot payload.
  /households/{household_id}/portable_imports/dry_run:
    post:
      operationId: dryRunPortableImport
      tags:
        - Household
        - Portability
      summary: Validate an encrypted portable import without writing data.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Import validation summary.
  /households/{household_id}/portable_imports:
    post:
      operationId: createPortableImport
      tags:
        - Household
        - Portability
      summary: Apply an encrypted portable import transactionally.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Import applied.
  /households/{household_id}/data_exports/{mode}:
    get:
      operationId: getDataExport
      tags:
        - Household
        - Portability
      summary: Export household data in a selected backup/profile mode.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - name: mode
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Export payload.
  /households/{household_id}/sync/snapshot:
    get:
      operationId: getSyncSnapshot
      tags:
        - Household
        - Synchronization
      summary: Read a portable v2 sync snapshot.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Sync snapshot.
  /households/{household_id}/sync/changes:
    get:
      operationId: getSyncChanges
      tags:
        - Household
        - Synchronization
      summary: Read sync changes after a cursor.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Change feed.
  /households/{household_id}/sync/batches:
    post:
      operationId: createSyncBatch
      tags:
        - Household
        - Synchronization
      summary: Apply transactional sync batch operations.
      parameters:
        - $ref: '#/components/parameters/household_id'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SyncBatchRequest'
      responses:
        '201':
          description: Batch applied.
        '409':
          $ref: '#/components/responses/Conflict'
        '428':
          $ref: '#/components/responses/PreconditionRequired'
  /households/{household_id}/admin/settings:
    get:
      operationId: getHouseholdAdminSettings
      tags:
        - Household administration
        - Household settings
      summary: Read household administration settings.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Household settings.
    patch:
      operationId: updateHouseholdAdminSettings
      tags:
        - Household administration
        - Household settings
      summary: Update household administration settings.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Household settings updated.
    put:
      operationId: replaceHouseholdAdminSettings
      tags:
        - Household administration
        - Household settings
      summary: Replace household administration settings.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Household settings updated.
  /households/{household_id}/admin/memberships:
    get:
      operationId: listMemberships
      tags:
        - Household administration
        - Memberships
      summary: List household memberships.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Membership collection.
  /households/{household_id}/admin/memberships/{id}:
    patch:
      operationId: updateMembership
      tags:
        - Household administration
        - Memberships
      summary: Update a household membership.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: Membership updated.
    put:
      operationId: replaceMembership
      tags:
        - Household administration
        - Memberships
      summary: Replace a household membership.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      responses:
        '200':
          description: Membership updated.
    delete:
      operationId: deleteMembership
      tags:
        - Household administration
        - Memberships
      summary: Revoke a household membership.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: Membership revoked.
  /households/{household_id}/admin/invitations:
    get:
      operationId: listInvitations
      tags:
        - Household administration
        - Invitations
      summary: List household invitations.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Invitation collection.
    post:
      operationId: createInvitation
      tags:
        - Household administration
        - Invitations
      summary: Create a household invitation.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Invitation created.
  /households/{household_id}/admin/invitations/{id}:
    delete:
      operationId: deleteInvitation
      tags:
        - Household administration
        - Invitations
      summary: Revoke a household invitation.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: Invitation revoked.
  /households/{household_id}/admin/person_access_grants:
    get:
      operationId: listPersonAccessGrants
      tags:
        - Household administration
        - Access grants
      summary: List person access grants.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Access grant collection.
    post:
      operationId: createPersonAccessGrant
      tags:
        - Household administration
        - Access grants
      summary: Create a person access grant.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: Access grant created.
  /households/{household_id}/admin/person_access_grants/{id}:
    delete:
      operationId: deletePersonAccessGrant
      tags:
        - Household administration
        - Access grants
      summary: Revoke a person access grant.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: Access grant revoked.
  /households/{household_id}/admin/app_tokens:
    get:
      operationId: listAppTokens
      tags:
        - Household administration
        - App tokens
      summary: List API app tokens.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: API app token collection.
    post:
      operationId: createAppToken
      tags:
        - Household administration
        - App tokens
      summary: Create an API app token.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '201':
          description: API app token created.
  /households/{household_id}/admin/app_tokens/{id}:
    delete:
      operationId: deleteAppToken
      tags:
        - Household administration
        - App tokens
      summary: Revoke an API app token.
      parameters:
        - $ref: '#/components/parameters/household_id'
        - $ref: '#/components/parameters/id'
      responses:
        '204':
          description: API app token revoked.
  /households/{household_id}/admin/audit_logs:
    get:
      operationId: listAuditLogs
      tags:
        - Household administration
        - Audit logs
      summary: List household security audit events.
      parameters:
        - $ref: '#/components/parameters/household_id'
      responses:
        '200':
          description: Audit event collection.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: opaque
  parameters:
    household_id:
      name: household_id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/NumericId'
    id:
      name: id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/NumericId'
    resource_id:
      name: id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/ResourceIdentifier'
    native_device_token:
      name: id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/DeviceTokenIdentifier'
    page:
      name: page
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        default: 1
    per_page:
      name: per_page
      in: query
      required: false
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    updated_since:
      name: updated_since
      in: query
      required: false
      schema:
        $ref: '#/components/schemas/Timestamp'
    if_match:
      name: If-Match
      in: header
      required: false
      schema:
        type: string
  headers:
    etag:
      description: Version identifier for optimistic concurrency.
      required: true
      schema:
        type: string
    retry_after:
      description: Seconds until the request may be retried.
      required: true
      schema:
        type: integer
        minimum: 1
  responses:
    Unauthorized:
      description: The bearer credential is absent, invalid, expired, revoked, or no longer active.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Forbidden:
      description: The credential cannot perform the operation in the routed household or person scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    NotFound:
      description: The resource does not exist inside the authorized scope.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    ValidationFailed:
      description: The submitted attributes or filters are invalid.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    Conflict:
      description: The resource or idempotency state conflicts with the requested mutation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    PreconditionRequired:
      description: The mutation requires a version precondition.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    RateLimited:
      description: The request exceeded an API rate limit.
      headers:
        Retry-After:
          $ref: '#/components/headers/retry_after'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimitErrorEnvelope'
  schemas:
    NumericId:
      type: integer
      minimum: 1
    PortableId:
      type: string
      format: uuid
    ResourceIdentifier:
      oneOf:
        - $ref: '#/components/schemas/NumericId'
        - $ref: '#/components/schemas/PortableId'
    DeviceTokenIdentifier:
      type: string
      minLength: 1
    NullableNumericId:
      oneOf:
        - $ref: '#/components/schemas/NumericId'
        - type: 'null'
    NullablePortableId:
      oneOf:
        - $ref: '#/components/schemas/PortableId'
        - type: 'null'
    Timestamp:
      type: string
      format: date-time
    Date:
      type: string
      format: date
    PaginationMeta:
      type: object
      additionalProperties: false
      required:
        - page
        - per_page
        - total_count
      properties:
        page:
          type: integer
          minimum: 1
        per_page:
          type: integer
          minimum: 1
          maximum: 100
        total_count:
          type: integer
          minimum: 0
    PersonAttributes:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
          minLength: 1
        email:
          type:
            - string
            - 'null'
          format: email
        date_of_birth:
          $ref: '#/components/schemas/Date'
        person_type:
          type: string
          enum:
            - adult
            - minor
            - dependent_adult
        has_capacity:
          type: boolean
    Person:
      type: object
      additionalProperties: false
      required:
        - id
        - portable_id
        - updated_at
        - name
        - email
        - date_of_birth
        - person_type
        - has_capacity
        - age
        - location_ids
        - location_portable_ids
        - notification_preference_id
        - notification_preference_portable_id
      properties:
        id:
          $ref: '#/components/schemas/NumericId'
        portable_id:
          $ref: '#/components/schemas/PortableId'
        updated_at:
          $ref: '#/components/schemas/Timestamp'
        name:
          type: string
          minLength: 1
        email:
          type:
            - string
            - 'null'
          format: email
        date_of_birth:
          oneOf:
            - $ref: '#/components/schemas/Date'
            - type: 'null'
        person_type:
          type: string
          enum:
            - adult
            - minor
            - dependent_adult
        has_capacity:
          type: boolean
        age:
          type:
            - integer
            - 'null'
        location_ids:
          type: array
          items:
            $ref: '#/components/schemas/NumericId'
        location_portable_ids:
          type: array
          items:
            $ref: '#/components/schemas/PortableId'
        notification_preference_id:
          $ref: '#/components/schemas/NullableNumericId'
        notification_preference_portable_id:
          $ref: '#/components/schemas/NullablePortableId'
    PersonCreateRequest:
      type: object
      additionalProperties: false
      required:
        - person
      properties:
        person:
          allOf:
            - $ref: '#/components/schemas/PersonAttributes'
            - required:
                - name
                - date_of_birth
    PersonUpdateRequest:
      type: object
      additionalProperties: false
      required:
        - person
      properties:
        person:
          $ref: '#/components/schemas/PersonAttributes'
    PersonResponse:
      type: object
      additionalProperties: false
      required:
        - data
      properties:
        data:
          $ref: '#/components/schemas/Person'
    PersonCollectionResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Person'
        meta:
          $ref: '#/components/schemas/PaginationMeta'
    ValidationErrors:
      type: object
      additionalProperties:
        type: array
        items:
          type: string
    Error:
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - request_id
      properties:
        code:
          type: string
          minLength: 1
        message:
          type: string
          minLength: 1
        request_id:
          type: string
          minLength: 1
        errors:
          $ref: '#/components/schemas/ValidationErrors'
    ErrorEnvelope:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/Error'
    RateLimitError:
      type: object
      additionalProperties: false
      required:
        - code
        - message
      properties:
        code:
          type: string
          const: rate_limited
        message:
          type: string
          minLength: 1
    RateLimitErrorEnvelope:
      type: object
      additionalProperties: false
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/RateLimitError'
    SyncBatchRequest:
      type: object
      additionalProperties: false
      required:
        - batch
      properties:
        batch:
          type: object
          additionalProperties: false
          required:
            - operations
          properties:
            operations:
              type: array
              minItems: 1
              items:
                $ref: '#/components/schemas/SyncBatchOperation'
    SyncBatchOperation:
      type: object
      additionalProperties: false
      required:
        - action
        - resource_type
        - id
        - if_match
      properties:
        action:
          type: string
          enum:
            - update
            - delete
        resource_type:
          type: string
          enum:
            - medication
            - health_event
        id:
          $ref: '#/components/schemas/ResourceIdentifier'
        if_match:
          type: string
          description: Exact ETag from the snapshot or latest resource response.
        attributes:
          type: object
          additionalProperties: true
