Skip to main content

API Endpoints

All endpoints require authentication and are prefixed with /v3/order:
  • POST /v3/order/psc/appointment/availability - Get available time slots
  • POST /v3/order/{order_id}/psc/appointment/book - Book an appointment
  • PATCH /v3/order/{order_id}/psc/appointment/reschedule - Reschedule an appointment
  • PATCH /v3/order/{order_id}/psc/appointment/cancel - Cancel an appointment
  • GET /v3/order/{order_id}/psc/appointment - Get appointment details
  • GET /v3/order/psc/appointment/cancellation-reasons - Get cancellation reasons

Environment Differences

⚠️ Important: Currently, error behavior differs between sandbox and production environments:
EnvironmentQuest IntegrationSlot Conflict Behavior
SandboxMock clientReturns 400 (mock doesn’t simulate conflicts)
ProductionReal Quest APIReturns 404 (actual Quest response)
Recommendation: For now, handle both 400 and 404 responses as potential slot-unavailability scenarios.

Frequently Asked Questions

Q: Do I need to parse error messages to distinguish between different 400 errors?

Yes. Since multiple error types return 400 status codes, you must examine the error message content to determine the appropriate handling:
  • Time in the past: "This appointment slot is no longer available. Please select a new time." → Select new slot
  • Invalid booking key: "Invalid booking key format" → Fix request
  • Missing parameters: "radius must be provided when using zip_code" → Fix request
  • Order state issues: "Order is not in a state that allows booking..." → Contact support
Example of message-based routing:
if (error.status === 400) {
  if (error.message.includes("no longer available")) {
    // Time in past - show slot picker
    return { action: 'SELECT_NEW_SLOT' };
  } else if (error.message.includes("Invalid booking key")) {
    // Bad booking key - refresh availability 
    return { action: 'REFRESH_AVAILABILITY' };
  } else if (error.message.includes("not in a state")) {
    // Order workflow issue - contact support
    return { action: 'CONTACT_SUPPORT' };
  }
  // ... handle other 400 scenarios
}
Why this is necessary: HTTP status codes alone are insufficient because our API uses 400 for multiple distinct error categories that require different user experiences.

Error Categories & Handling

Slot Unavailability Errors

The user should select a different time slot
StatusError MessageWhen It OccursUser Action
400"This appointment slot is no longer available. Please select a new time."Requested time is in the pastSelect a future time slot
404Quest-specific error messagesSlot already taken or no longer existsSelect a different time slot
404"No slots found for zip code"No locations/slots in the areaTry a different location or date range
404"No slots found. Not all PSCs support scheduling..."All locations lack scheduling capabilityUse different locations
Implementation Example:
if (error.status === 404 || 
    error.message?.includes("no longer available") || 
    error.message?.includes("No slots found")) {
  showMessage("This time slot is no longer available. Please select a different time.");
  redirectToSlotSelection();
}

Input Validation Errors

Fix the request parameters
StatusError MessageWhen It OccursUser Action
400"Invalid booking key format"Malformed or corrupted booking keyRefresh availability and get a new booking key
400"Location with site code {code} not found"Invalid PSC location codeUse a valid site code from the availability API
400"Location with site code {code} is no longer active"PSC location disabledSelect a different location
400"start_date must be greater or equal than the current date"Past date in the availability requestUse current or future date
400"site_codes or zip_code must be provided"Missing search parametersProvide either site codes or zip code
400"radius must be provided when using zip_code"Missing radius with zip code searchInclude radius parameter
400"site_codes must be less than or equal to 3"Too many site codesLimit to 3 site codes maximum
404"This order doesn't exist."Invalid order IDVerify order ID is correct
404"This order is not a walk-in phlebotomy order."Wrong order typeUse the correct order type
Implementation Example:
if (error.status === 400 && 
    (error.message?.includes("Invalid booking key") || 
     error.message?.includes("not found") ||
     error.message?.includes("must be provided"))) {
  showMessage("Please check your request and try again.");
  // Log error for debugging
  console.error("Input validation error:", error.message);
}

Order State Errors

Order workflow or business logic issues
StatusError MessageWhen It OccursUser Action
400"Order is not a walkin phlebotomy order"Wrong order type for PSC bookingContact support
400"Order does not have a sample ID..."Order missing requisitionWait for requisition or contact support
400"Order is not in a state that allows booking..."Order workflow prevents bookingContact support
400"Order is not in a state that allows cancelling..."Order workflow prevents cancellationContact support
400"Order is not in a state that allows rescheduling..."Order workflow prevents reschedulingContact support
400"This order does not have a requisition..."Missing requisition for appointmentWait or contact support
400"This lab is not supported."Order not for Quest labUse a supported lab
Implementation Example:
if (error.status === 400 && 
    (error.message?.includes("not in a state") || 
     error.message?.includes("sample ID") ||
     error.message?.includes("requisition"))) {
  showMessage("There's an issue with your order. Please contact support.");
  enableContactSupport();
}

Appointment Management Errors

Appointment-specific business rules
StatusError MessageWhen It OccursUser Action
400"This order doesn't have an appointment yet."Trying to modify a non-existent appointmentBook an appointment first
400"This appointment cannot be rescheduled."Appointment marked non-reschedulableCancel and book a new appointment
400"This appointment has been cancelled or completed."Operating on finalized appointmentNo further action is possible
400"This appointment has already been cancelled."Cancelling already cancelled appointmentNo action needed
404"Appointment not found."Appointment doesn’t existVerify appointment exists
Implementation Example:
if (error.status === 400 && 
    error.message?.includes("appointment")) {
  if (error.message.includes("cannot be rescheduled")) {
    showMessage("This appointment cannot be rescheduled. You can cancel and book a new one.");
    showCancelAndRebookOption();
  } else if (error.message.includes("already been cancelled")) {
    showMessage("This appointment has already been cancelled.");
    hideAppointmentActions();
  }
}

Authorization Errors

Access control issues
StatusError MessageWhen It OccursUser Action
401Authentication errorInvalid or expired tokenRe-authenticate
403"Action forbidden"User lacks permission for the orderContact support

Service Availability Errors

System configuration or availability
StatusError MessageWhen It OccursUser Action
503"Feature not enabled"PSC scheduling disabledContact support or try later

Comprehensive Error Handling Implementation

function handleQuestPSCError(error) {
  const status = error.status || error.response?.status;
  const message = error.message || error.detail || error.response?.data?.detail || '';

  // Slot unavailability - suggest new slot
  if (status === 404 || 
      message.includes("no longer available") || 
      message.includes("No slots found")) {
    return {
      category: 'SLOT_UNAVAILABLE',
      userMessage: 'This time slot is no longer available. Please select a different time.',
      action: 'SELECT_NEW_SLOT',
      retry: false
    };
  }

  // Input validation errors - fix request
  if (status === 400 && (
      message.includes("Invalid booking key") ||
      message.includes("not found") ||
      message.includes("must be provided") ||
      message.includes("must be less than") ||
      message.includes("greater or equal"))) {
    return {
      category: 'INPUT_VALIDATION',
      userMessage: 'Please check your request and try again.',
      action: 'FIX_REQUEST',
      retry: true
    };
  }

  // Order state errors - contact support
  if (status === 400 && (
      message.includes("not in a state") ||
      message.includes("sample ID") ||
      message.includes("requisition") ||
      message.includes("not a walkin"))) {
    return {
      category: 'ORDER_STATE',
      userMessage: 'There\'s an issue with your order. Please contact support.',
      action: 'CONTACT_SUPPORT',
      retry: false
    };
  }

  // Appointment management errors
  if (status === 400 && message.includes("appointment")) {
    if (message.includes("cannot be rescheduled")) {
      return {
        category: 'APPOINTMENT_RULE',
        userMessage: 'This appointment cannot be rescheduled. You can cancel and book a new one.',
        action: 'CANCEL_AND_REBOOK',
        retry: false
      };
    } else if (message.includes("already been cancelled")) {
      return {
        category: 'APPOINTMENT_STATE',
        userMessage: 'This appointment has already been cancelled.',
        action: 'NO_ACTION',
        retry: false
      };
    }
  }

  // Authorization errors
  if (status === 401 || status === 403) {
    return {
      category: 'AUTHORIZATION',
      userMessage: 'You don\'t have permission to perform this action.',
      action: 'REAUTH_OR_CONTACT_SUPPORT',
      retry: false
    };
  }

  // Service unavailable
  if (status === 503) {
    return {
      category: 'SERVICE_UNAVAILABLE',
      userMessage: 'This feature is temporarily unavailable. Please try again later.',
      action: 'TRY_LATER',
      retry: true
    };
  }

  // Default fallback
  return {
    category: 'UNKNOWN',
    userMessage: 'An unexpected error occurred. Please contact support.',
    action: 'CONTACT_SUPPORT',
    retry: false
  };
}

Testing Error Scenarios

Sandbox Testing

  • Most validation errors can be tested in sandbox
  • Slot conflicts cannot be reliably tested (mock client always succeeds)
  • Use invalid parameters to test input validation

Production Testing

  • Test with caution using real appointments
  • Slot conflicts will return actual 404 errors from Quest
  • Time-based errors can be tested with past dates

Best Practices

  1. Always check error messages, not just status codes
  2. Handle both 400 and 404 for slot unavailability due to environment differences
  3. Provide specific user guidance based on error category
  4. Log errors with context for debugging
  5. Implement retry logic only for appropriate error types
  6. Test error handling in both sandbox and production environments

Quest API Limitations

  • Timeout: 15 seconds read timeout, 5 seconds connection timeout
  • Slot availability: Real-time, can change between availability check and booking
  • Mock client: Doesn’t simulate all real-world scenarios
  • Rate limiting: Standard Quest API rate limits apply

For technical support or questions about this integration, contact your Junction support team.