openapi: 3.1.0
info:
  title: JustLabs Public Quote API
  version: 1.0.0
  summary: All-in prices and state availability for JustLabs lab tests. Read-only, no authentication.
  description: |
    JustLabs sells Quest Diagnostics lab tests direct to consumers. An order costs the sum of its
    test/panel prices plus ONE flat per-order fee (Quest blood draw + independent physician order),
    charged once however many items are in the order. These endpoints compute that total with the
    same code checkout charges with, so AI assistants and comparison sites can state an exact
    all-in price instead of adding fees by hand.

    - Item ids come from the catalogue: https://justlabs.health/api/catalog (id, name, price, url
      per test and panel). This API does not re-list the catalogue.
    - All money is integer cents, USD.
    - No personal or health information is accepted or returned.
    - Rate limited per client IP (HTTP 429 with Retry-After).
    - The authenticated partner ordering API is separate: https://justlabs.health/api-docs/openapi.yaml
  contact:
    email: support@justlabs.health
    url: https://justlabs.health/contact
servers:
  - url: https://justlabs.health
paths:
  /api/public/v1/quote:
    get:
      operationId: getQuote
      summary: All-in price of a basket of tests and panels
      description: |
        Returns each item's price, the subtotal, the single per-order fee and the total. A panel is
        priced at its own (bundle) price. Pass `state` to learn whether the order can be placed there.
      parameters:
        - name: items
          in: query
          required: true
          description: Comma-separated catalogue ids (max 50). Duplicates are ignored.
          schema: { type: string, example: 'tsh,vitamin-d' }
        - name: state
          in: query
          required: false
          description: 2-letter US state code (50 states + DC) where the patient will order.
          schema: { type: string, pattern: '^[A-Za-z]{2}$', example: CA }
      responses:
        '200':
          description: The quote.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Quote' }
              example:
                currency: USD
                items:
                  - { id: tsh, type: test, name: 'TSH (Thyroid Stimulating Hormone)', url: 'https://justlabs.health/tests/tsh', unitPriceCents: 900, quantity: 1 }
                  - { id: vitamin-d, type: test, name: 'Vitamin D, 25-Hydroxy', url: 'https://justlabs.health/tests/vitamin-d', unitPriceCents: 2500, quantity: 1 }
                subtotalCents: 3400
                orderFee: { label: 'Lab & Physician Order Fee', description: 'Quest Diagnostics blood draw and independent physician order, once per order', amountCents: 1800, chargedPer: order }
                totalCents: 5200
                eligibility: { state: CA, available: true, reason: null, message: null, unavailableItems: [] }
                checkoutUrl: 'https://justlabs.health/cart/handoff?items=tsh,vitamin-d'
                updatedAt: '2026-10-16T15:00:00.000Z'
                restrictedStates: [NY, NJ, RI, HI]
                catalogUrl: 'https://justlabs.health/api/catalog'
        '400': { $ref: '#/components/responses/BadRequest' }
        '429': { $ref: '#/components/responses/RateLimited' }
  /api/public/v1/availability:
    get:
      operationId: getAvailability
      summary: Can an order be placed from this state (optionally, for this item)?
      parameters:
        - name: state
          in: query
          required: true
          schema: { type: string, pattern: '^[A-Za-z]{2}$', example: CA }
        - name: item
          in: query
          required: false
          description: One catalogue id.
          schema: { type: string, example: tsh }
      responses:
        '200':
          description: Availability.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Availability' }
              example:
                state: NY
                available: false
                reason: state_restricted
                message: JustLabs does not accept lab orders from this state.
                item: null
                restrictedStates: [NY, NJ, RI, HI]
                catalogUrl: 'https://justlabs.health/api/catalog'
                updatedAt: '2026-10-16T15:00:00.000Z'
        '400': { $ref: '#/components/responses/BadRequest' }
        '429': { $ref: '#/components/responses/RateLimited' }
components:
  responses:
    BadRequest:
      description: Invalid input. Deterministic for the same input.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
    RateLimited:
      description: Too many requests from this client.
      headers:
        Retry-After: { schema: { type: integer } }
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
  schemas:
    UnavailableReason:
      type: string
      enum: [state_restricted, test_not_offered_in_state]
    QuoteItem:
      type: object
      required: [id, type, name, url, unitPriceCents, quantity]
      properties:
        id: { type: string, description: Stable catalogue id. }
        type: { type: string, enum: [test, panel] }
        name: { type: string }
        url: { type: string, format: uri }
        unitPriceCents: { type: integer, minimum: 1 }
        quantity: { type: integer, enum: [1] }
    OrderFee:
      type: object
      required: [label, description, amountCents, chargedPer]
      properties:
        label: { type: string }
        description: { type: string }
        amountCents: { type: integer, minimum: 0 }
        chargedPer: { type: string, enum: [order] }
    Eligibility:
      type: object
      required: [state, available, reason, message, unavailableItems]
      properties:
        state: { type: string }
        available: { type: boolean }
        reason:
          oneOf:
            - { $ref: '#/components/schemas/UnavailableReason' }
            - { type: 'null' }
        message: { type: [string, 'null'] }
        unavailableItems:
          type: array
          items:
            type: object
            required: [id, reason]
            properties:
              id: { type: string }
              reason: { $ref: '#/components/schemas/UnavailableReason' }
    Quote:
      type: object
      required: [currency, items, subtotalCents, orderFee, totalCents, eligibility, checkoutUrl, updatedAt, restrictedStates, catalogUrl]
      properties:
        currency: { type: string, enum: [USD] }
        items:
          type: array
          minItems: 1
          items: { $ref: '#/components/schemas/QuoteItem' }
        subtotalCents: { type: integer, minimum: 0, description: Sum of unitPriceCents. }
        orderFee: { $ref: '#/components/schemas/OrderFee' }
        totalCents: { type: integer, minimum: 0, description: subtotalCents + orderFee.amountCents — what checkout charges before any discount code. }
        eligibility:
          description: null when no state was given.
          oneOf:
            - { $ref: '#/components/schemas/Eligibility' }
            - { type: 'null' }
        checkoutUrl: { type: string, format: uri, description: Opens a cart prefilled with these items. }
        updatedAt: { type: string, format: date-time, description: When the quote was computed from the live catalogue. }
        restrictedStates: { type: array, items: { type: string } }
        catalogUrl: { type: string, format: uri }
    Availability:
      type: object
      required: [state, available, reason, message, item, restrictedStates, catalogUrl, updatedAt]
      properties:
        state: { type: string }
        available: { type: boolean }
        reason:
          oneOf:
            - { $ref: '#/components/schemas/UnavailableReason' }
            - { type: 'null' }
        message: { type: [string, 'null'] }
        item:
          oneOf:
            - type: object
              required: [id, available, reason, message]
              properties:
                id: { type: string }
                available: { type: boolean }
                reason:
                  oneOf:
                    - { $ref: '#/components/schemas/UnavailableReason' }
                    - { type: 'null' }
                message: { type: [string, 'null'] }
            - { type: 'null' }
        restrictedStates: { type: array, items: { type: string } }
        catalogUrl: { type: string, format: uri }
        updatedAt: { type: string, format: date-time }
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, docs]
          properties:
            code:
              type: string
              enum: [missing_items, too_many_items, invalid_item_id, unknown_items, invalid_state, missing_state, rate_limited]
            message: { type: string }
            ids: { type: array, items: { type: string }, description: The offending ids, when relevant. }
            docs: { type: string, format: uri }
