crisphive 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +90 -11
  3. data/crisphive.gemspec +5 -5
  4. data/lib/crisphive/api/business_skill_api.rb +154 -8
  5. data/lib/crisphive/api/customer_api.rb +15 -12
  6. data/lib/crisphive/api/job_request_business_api.rb +1002 -112
  7. data/lib/crisphive/api/job_types_api.rb +6 -2
  8. data/lib/crisphive/api/service_area_api.rb +6 -6
  9. data/lib/crisphive/api/technician_api.rb +510 -6
  10. data/lib/crisphive/api/vehicle_api.rb +6 -6
  11. data/lib/crisphive/api_client.rb +2 -2
  12. data/lib/crisphive/api_error.rb +2 -2
  13. data/lib/crisphive/configuration.rb +3 -3
  14. data/lib/crisphive/models/commit_emergency_reschedule200_response.rb +253 -0
  15. data/lib/crisphive/models/commit_job_request_move200_response.rb +253 -0
  16. data/lib/crisphive/models/create_customer200_response.rb +5 -2
  17. data/lib/crisphive/models/create_job_request200_response.rb +5 -2
  18. data/lib/crisphive/models/create_technician200_response.rb +253 -0
  19. data/lib/crisphive/models/customer.rb +2 -2
  20. data/lib/crisphive/models/customer_address.rb +2 -2
  21. data/lib/crisphive/models/customer_address_request.rb +11 -2
  22. data/lib/crisphive/models/customer_contact.rb +2 -2
  23. data/lib/crisphive/models/customer_create_request.rb +2 -2
  24. data/lib/crisphive/models/customer_create_response.rb +2 -2
  25. data/lib/crisphive/models/customer_list.rb +3 -3
  26. data/lib/crisphive/models/customer_list_item.rb +2 -2
  27. data/lib/crisphive/models/customer_profile.rb +2 -2
  28. data/lib/crisphive/models/customer_spending.rb +2 -2
  29. data/lib/crisphive/models/customer_tier_progress.rb +2 -2
  30. data/lib/crisphive/models/customer_update_request.rb +2 -2
  31. data/lib/crisphive/models/get_customer200_response.rb +5 -2
  32. data/lib/crisphive/models/get_job_request200_response.rb +5 -2
  33. data/lib/crisphive/models/get_job_request_timeline200_response.rb +5 -2
  34. data/lib/crisphive/models/get_job_type200_response.rb +5 -2
  35. data/lib/crisphive/models/get_service_area200_response.rb +5 -2
  36. data/lib/crisphive/models/get_technician200_response.rb +5 -2
  37. data/lib/crisphive/models/get_technician_schedule200_response.rb +253 -0
  38. data/lib/crisphive/models/get_vehicle200_response.rb +5 -2
  39. data/lib/crisphive/models/job_date.rb +5 -3
  40. data/lib/crisphive/models/job_date_business_range.rb +7 -3
  41. data/lib/crisphive/models/job_date_period.rb +2 -2
  42. data/lib/crisphive/models/job_date_period_entry.rb +4 -2
  43. data/lib/crisphive/models/job_request.rb +46 -6
  44. data/lib/crisphive/models/job_request_action_audit_by.rb +2 -2
  45. data/lib/crisphive/models/job_request_action_audit_entry.rb +3 -3
  46. data/lib/crisphive/models/job_request_action_summary.rb +2 -2
  47. data/lib/crisphive/models/job_request_address_summary.rb +2 -2
  48. data/lib/crisphive/models/job_request_archive_summary.rb +2 -2
  49. data/lib/crisphive/models/job_request_arrival_window.rb +4 -4
  50. data/lib/crisphive/models/job_request_assigned_vehicle.rb +2 -2
  51. data/lib/crisphive/models/job_request_assignment_summary.rb +2 -2
  52. data/lib/crisphive/models/job_request_attention_summary.rb +259 -0
  53. data/lib/crisphive/models/job_request_available_vehicle.rb +235 -0
  54. data/lib/crisphive/models/job_request_booking_windows.rb +2 -2
  55. data/lib/crisphive/models/job_request_buddy_slot_candidates.rb +239 -0
  56. data/lib/crisphive/models/job_request_business_time_range.rb +3 -3
  57. data/lib/crisphive/models/job_request_changes.rb +2 -2
  58. data/lib/crisphive/models/job_request_confirm_request.rb +245 -0
  59. data/lib/crisphive/models/job_request_create_request.rb +60 -6
  60. data/lib/crisphive/models/job_request_create_response.rb +2 -2
  61. data/lib/crisphive/models/job_request_crew_candidates.rb +227 -0
  62. data/lib/crisphive/models/job_request_crew_change.rb +299 -0
  63. data/lib/crisphive/models/job_request_crew_member.rb +7 -4
  64. data/lib/crisphive/models/job_request_crew_member_input.rb +284 -0
  65. data/lib/crisphive/models/job_request_crew_recommendation.rb +255 -0
  66. data/lib/crisphive/models/job_request_customer_summary.rb +2 -2
  67. data/lib/crisphive/models/job_request_day_windows.rb +3 -3
  68. data/lib/crisphive/models/job_request_emergency_candidate.rb +297 -0
  69. data/lib/crisphive/models/job_request_emergency_candidates.rb +311 -0
  70. data/lib/crisphive/models/job_request_emergency_candidates_request.rb +328 -0
  71. data/lib/crisphive/models/job_request_emergency_commit_request.rb +351 -0
  72. data/lib/crisphive/models/job_request_emergency_plan.rb +345 -0
  73. data/lib/crisphive/models/job_request_emergency_preview_request.rb +329 -0
  74. data/lib/crisphive/models/job_request_job_date_business_range_request.rb +3 -3
  75. data/lib/crisphive/models/job_request_job_date_period_request.rb +2 -2
  76. data/lib/crisphive/models/job_request_job_date_request.rb +3 -3
  77. data/lib/crisphive/models/job_request_lead_candidate.rb +311 -0
  78. data/lib/crisphive/models/job_request_list.rb +2 -2
  79. data/lib/crisphive/models/job_request_move_commit_req.rb +343 -0
  80. data/lib/crisphive/models/job_request_move_member.rb +299 -0
  81. data/lib/crisphive/models/job_request_move_plan.rb +377 -0
  82. data/lib/crisphive/models/job_request_move_preview_req.rb +290 -0
  83. data/lib/crisphive/models/job_request_move_warning.rb +311 -0
  84. data/lib/crisphive/models/job_request_period.rb +2 -2
  85. data/lib/crisphive/models/job_request_quote_request.rb +340 -0
  86. data/lib/crisphive/models/job_request_quote_summary.rb +2 -2
  87. data/lib/crisphive/models/job_request_rating_summary.rb +2 -2
  88. data/lib/crisphive/models/job_request_reschedule_day.rb +227 -0
  89. data/lib/crisphive/models/job_request_reschedule_move.rb +319 -0
  90. data/lib/crisphive/models/job_request_reschedule_reassignment.rb +339 -0
  91. data/lib/crisphive/models/job_request_schedule.rb +2 -2
  92. data/lib/crisphive/models/job_request_session.rb +8 -7
  93. data/lib/crisphive/models/job_request_skill_summary.rb +2 -2
  94. data/lib/crisphive/models/job_request_status_summary.rb +2 -2
  95. data/lib/crisphive/models/job_request_tech_candidate.rb +287 -0
  96. data/lib/crisphive/models/job_request_technician_info.rb +2 -2
  97. data/lib/crisphive/models/job_request_time_segments.rb +287 -0
  98. data/lib/crisphive/models/job_request_time_segments_business_time.rb +321 -0
  99. data/lib/crisphive/models/job_request_time_segments_day.rb +247 -0
  100. data/lib/crisphive/models/job_request_time_segments_slot.rb +375 -0
  101. data/lib/crisphive/models/job_request_time_segments_worker.rb +295 -0
  102. data/lib/crisphive/models/job_request_timeline.rb +2 -2
  103. data/lib/crisphive/models/job_request_timeline_action.rb +2 -2
  104. data/lib/crisphive/models/job_request_timeline_event.rb +2 -2
  105. data/lib/crisphive/models/job_request_update_priority_request.rb +295 -0
  106. data/lib/crisphive/models/job_type.rb +2 -2
  107. data/lib/crisphive/models/list_crew_candidates200_response.rb +253 -0
  108. data/lib/crisphive/models/list_customers200_response.rb +5 -2
  109. data/lib/crisphive/models/list_emergency_candidates200_response.rb +253 -0
  110. data/lib/crisphive/models/list_job_request_booking_windows200_response.rb +5 -2
  111. data/lib/crisphive/models/list_job_request_changes200_response.rb +5 -2
  112. data/lib/crisphive/models/list_job_requests200_response.rb +5 -2
  113. data/lib/crisphive/models/list_job_types200_response.rb +5 -2
  114. data/lib/crisphive/models/list_matching_slots200_response.rb +253 -0
  115. data/lib/crisphive/models/list_nearby_technicians200_response.rb +253 -0
  116. data/lib/crisphive/models/list_service_areas200_response.rb +5 -2
  117. data/lib/crisphive/models/list_skill_categories200_response.rb +5 -2
  118. data/lib/crisphive/models/list_skills200_response.rb +5 -2
  119. data/lib/crisphive/models/list_skills_by_category200_response.rb +5 -2
  120. data/lib/crisphive/models/list_technician_skills200_response.rb +253 -0
  121. data/lib/crisphive/models/list_technicians200_response.rb +5 -2
  122. data/lib/crisphive/models/list_vehicles200_response.rb +5 -2
  123. data/lib/crisphive/models/nearby_technician.rb +319 -0
  124. data/lib/crisphive/models/nearby_technicians.rb +237 -0
  125. data/lib/crisphive/models/pagination.rb +7 -2
  126. data/lib/crisphive/models/replace_technician_service_areas200_response.rb +253 -0
  127. data/lib/crisphive/models/response_envelope.rb +6 -2
  128. data/lib/crisphive/models/service_area.rb +2 -2
  129. data/lib/crisphive/models/service_area_list.rb +2 -2
  130. data/lib/crisphive/models/skill.rb +9 -2
  131. data/lib/crisphive/models/skill_by_category_list.rb +4 -2
  132. data/lib/crisphive/models/skill_category.rb +7 -2
  133. data/lib/crisphive/models/skill_category_list.rb +4 -2
  134. data/lib/crisphive/models/skill_list.rb +2 -2
  135. data/lib/crisphive/models/skill_summary.rb +2 -2
  136. data/lib/crisphive/models/technician.rb +2 -2
  137. data/lib/crisphive/models/technician_address.rb +2 -2
  138. data/lib/crisphive/models/technician_address_input.rb +408 -0
  139. data/lib/crisphive/models/technician_buddies_request.rb +236 -0
  140. data/lib/crisphive/models/technician_create_request.rb +600 -0
  141. data/lib/crisphive/models/technician_create_response.rb +215 -0
  142. data/lib/crisphive/models/technician_lead_ref.rb +2 -2
  143. data/lib/crisphive/models/technician_leads_request.rb +236 -0
  144. data/lib/crisphive/models/technician_list.rb +3 -3
  145. data/lib/crisphive/models/technician_schedule.rb +279 -0
  146. data/lib/crisphive/models/technician_schedule_block.rb +225 -0
  147. data/lib/crisphive/models/technician_schedule_session.rb +311 -0
  148. data/lib/crisphive/models/technician_service_area_ref.rb +2 -2
  149. data/lib/crisphive/models/technician_service_areas_request.rb +236 -0
  150. data/lib/crisphive/models/technician_service_areas_response.rb +217 -0
  151. data/lib/crisphive/models/technician_skill.rb +265 -0
  152. data/lib/crisphive/models/technician_skill_list.rb +227 -0
  153. data/lib/crisphive/models/technician_skills_request.rb +236 -0
  154. data/lib/crisphive/models/technician_status.rb +2 -2
  155. data/lib/crisphive/models/technician_update_request.rb +507 -0
  156. data/lib/crisphive/models/technician_vehicles_request.rb +236 -0
  157. data/lib/crisphive/models/vehicle.rb +26 -4
  158. data/lib/crisphive/models/vehicle_list.rb +2 -2
  159. data/lib/crisphive/models/vehicle_owner.rb +2 -2
  160. data/lib/crisphive/version.rb +3 -3
  161. data/lib/crisphive.rb +60 -2
  162. data/spec/spec_helper.rb +2 -2
  163. metadata +63 -5
@@ -1,7 +1,7 @@
1
1
  =begin
2
- #CrispHive Developer API
2
+ #Crisphive Developer API
3
3
 
4
- #Public REST API for integrating CrispHive from your own backend. Authenticate every request with a secret API key as a Bearer token (`Authorization: Bearer chsk_live_…`). The key prefix selects the data environment: `chsk_live_…` → production (live), `chsk_test_…` → sandbox (isolated test). **Key scopes (restricted keys).** A key is either *full-access* (can call every endpoint below) or *restricted* to a set of permission codes chosen at creation — the same codes as the dashboard permission grid (e.g. `customers_view`, `job_create`, `team_manage`). A restricted key calling an endpoint outside its scope gets `403`. The full code list is the permission catalog (`GET /permission/modules` on the dashboard API). Create, scope, and revoke keys from the business dashboard. Every response is wrapped in the envelope `{ \"error_code\": 0, \"message\": \"Success\", \"data\": <payload> }`.
4
+ #Public REST API for integrating Crisphive from your own backend. Authenticate every request with a secret API key as a Bearer token (`Authorization: Bearer chsk_live_…`). The key prefix selects the data environment: `chsk_live_…` → production (live), `chsk_test_…` → sandbox (isolated test). **Key scopes (restricted keys).** A key is either *full-access* (can call every endpoint below) or *restricted* to a set of permission codes chosen at creation — the same codes as the dashboard permission grid (e.g. `customers_view`, `job_create`, `team_manage`). A restricted key calling an endpoint outside its scope gets `403`. The full code list is the permission catalog (`GET /permission/modules` on the dashboard API). Create, scope, and revoke keys from the business dashboard. Every response is wrapped in the envelope `{ \"error_code\": 0, \"message\": \"Success\", \"data\": <payload> }`.
5
5
 
6
6
  The version of the OpenAPI document: 1.0
7
7
 
@@ -19,20 +19,249 @@ module Crisphive
19
19
  def initialize(api_client = ApiClient.default)
20
20
  @api_client = api_client
21
21
  end
22
- # Create a job request (business actor)
22
+ # Commit emergency insert + cascade reschedule
23
+ # Applies the cascade previewed by /emergency/preview: assigns the emergency job to the technician and pushes the displaced jobs back (or, with `displacement_mode=reassign`, re-staffs them onto their previewed alternates first), atomically. Supports Idempotency-Key. The server recomputes the plan under a lock and fences each job on its status_version — if anything changed since the preview it returns 409 EMERGENCY_RESCHEDULE_PLAN_DRIFTED (re-preview). Same body as preview + optional `emergency_expected_version`. Isolated feature (see EMERGENCY_RESCHEDULE_DESIGN.md). 409 NEXT STEPS: EMERGENCY_RESCHEDULE_PLAN_DRIFTED — the schedule changed between your preview and this commit (another booking/move won a lane): call /preview again, show the fresh plan, then commit. EMERGENCY_RESCHEDULE_SLOT_OCCUPIED — landing window blocked by an immovable anchor (P0/crew/multi-day): another tech or time. Other codes — same remedies as /candidates.
24
+ # @param job_request_emergency_commit_request [JobRequestEmergencyCommitRequest] emergency insert spec
25
+ # @param [Hash] opts the optional parameters
26
+ # @option opts [String] :idempotency_key Unique key making retries safe: a repeat send with the same key replays the original response (header Idempotent-Replayed: true) instead of re-running the operation. Reusing a key with a different body returns 422 IDEMPOTENCY_KEY_REUSE.
27
+ # @return [CommitEmergencyReschedule200Response]
28
+ def commit_emergency_reschedule(job_request_emergency_commit_request, opts = {})
29
+ data, _status_code, _headers = commit_emergency_reschedule_with_http_info(job_request_emergency_commit_request, opts)
30
+ data
31
+ end
32
+
33
+ # Commit emergency insert + cascade reschedule
34
+ # Applies the cascade previewed by /emergency/preview: assigns the emergency job to the technician and pushes the displaced jobs back (or, with &#x60;displacement_mode&#x3D;reassign&#x60;, re-staffs them onto their previewed alternates first), atomically. Supports Idempotency-Key. The server recomputes the plan under a lock and fences each job on its status_version — if anything changed since the preview it returns 409 EMERGENCY_RESCHEDULE_PLAN_DRIFTED (re-preview). Same body as preview + optional &#x60;emergency_expected_version&#x60;. Isolated feature (see EMERGENCY_RESCHEDULE_DESIGN.md). 409 NEXT STEPS: EMERGENCY_RESCHEDULE_PLAN_DRIFTED — the schedule changed between your preview and this commit (another booking/move won a lane): call /preview again, show the fresh plan, then commit. EMERGENCY_RESCHEDULE_SLOT_OCCUPIED — landing window blocked by an immovable anchor (P0/crew/multi-day): another tech or time. Other codes — same remedies as /candidates.
35
+ # @param job_request_emergency_commit_request [JobRequestEmergencyCommitRequest] emergency insert spec
36
+ # @param [Hash] opts the optional parameters
37
+ # @option opts [String] :idempotency_key Unique key making retries safe: a repeat send with the same key replays the original response (header Idempotent-Replayed: true) instead of re-running the operation. Reusing a key with a different body returns 422 IDEMPOTENCY_KEY_REUSE.
38
+ # @return [Array<(CommitEmergencyReschedule200Response, Integer, Hash)>] CommitEmergencyReschedule200Response data, response status code and response headers
39
+ def commit_emergency_reschedule_with_http_info(job_request_emergency_commit_request, opts = {})
40
+ if @api_client.config.debugging
41
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.commit_emergency_reschedule ...'
42
+ end
43
+ # verify the required parameter 'job_request_emergency_commit_request' is set
44
+ if @api_client.config.client_side_validation && job_request_emergency_commit_request.nil?
45
+ fail ArgumentError, "Missing the required parameter 'job_request_emergency_commit_request' when calling JobRequestBusinessApi.commit_emergency_reschedule"
46
+ end
47
+ # resource path
48
+ local_var_path = '/job-requests/emergency/commit'
49
+
50
+ # query parameters
51
+ query_params = opts[:query_params] || {}
52
+
53
+ # header parameters
54
+ header_params = opts[:header_params] || {}
55
+ # HTTP header 'Accept' (if needed)
56
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
57
+ # HTTP header 'Content-Type'
58
+ content_type = @api_client.select_header_content_type(['application/json'])
59
+ if !content_type.nil?
60
+ header_params['Content-Type'] = content_type
61
+ end
62
+ header_params[:'Idempotency-Key'] = opts[:'idempotency_key'] if !opts[:'idempotency_key'].nil?
63
+
64
+ # form parameters
65
+ form_params = opts[:form_params] || {}
66
+
67
+ # http body (model)
68
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_request_emergency_commit_request)
69
+
70
+ # return_type
71
+ return_type = opts[:debug_return_type] || 'CommitEmergencyReschedule200Response'
72
+
73
+ # auth_names
74
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
75
+
76
+ new_options = opts.merge(
77
+ :operation => :"JobRequestBusinessApi.commit_emergency_reschedule",
78
+ :header_params => header_params,
79
+ :query_params => query_params,
80
+ :form_params => form_params,
81
+ :body => post_body,
82
+ :auth_names => auth_names,
83
+ :return_type => return_type
84
+ )
85
+
86
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
87
+ if @api_client.config.debugging
88
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#commit_emergency_reschedule\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
89
+ end
90
+ return data, status_code, headers
91
+ end
92
+
93
+ # Commit a schedule-board job move
94
+ # Applies the move previewed by /move/preview: places the job on the technician at the new time and pushes the displaced jobs back, atomically (per-tech advisory lock; the server recomputes the plan and fences each job on its status_version — drift since the preview returns 409 SCHEDULE_MOVE_PLAN_DRIFTED, re-preview). Same body as preview + optional `expected_version`. See SCHEDULE_BOARD_DESIGN.md. 409 NEXT STEPS: SCHEDULE_MOVE_PLAN_DRIFTED — the schedule changed since your preview (or expected_move_ids no longer match): re-preview, show the fresh plan, commit again. All other codes — same remedies as /move/preview.
95
+ # @param id [String] Job request ID (UUID or short_code)
96
+ # @param job_request_move_commit_req [JobRequestMoveCommitReq] move spec
97
+ # @param [Hash] opts the optional parameters
98
+ # @option opts [String] :idempotency_key Unique key making retries safe: a repeat send with the same key replays the original response (header Idempotent-Replayed: true) instead of re-running the operation. Reusing a key with a different body returns 422 IDEMPOTENCY_KEY_REUSE.
99
+ # @return [CommitJobRequestMove200Response]
100
+ def commit_job_request_move(id, job_request_move_commit_req, opts = {})
101
+ data, _status_code, _headers = commit_job_request_move_with_http_info(id, job_request_move_commit_req, opts)
102
+ data
103
+ end
104
+
105
+ # Commit a schedule-board job move
106
+ # Applies the move previewed by /move/preview: places the job on the technician at the new time and pushes the displaced jobs back, atomically (per-tech advisory lock; the server recomputes the plan and fences each job on its status_version — drift since the preview returns 409 SCHEDULE_MOVE_PLAN_DRIFTED, re-preview). Same body as preview + optional &#x60;expected_version&#x60;. See SCHEDULE_BOARD_DESIGN.md. 409 NEXT STEPS: SCHEDULE_MOVE_PLAN_DRIFTED — the schedule changed since your preview (or expected_move_ids no longer match): re-preview, show the fresh plan, commit again. All other codes — same remedies as /move/preview.
107
+ # @param id [String] Job request ID (UUID or short_code)
108
+ # @param job_request_move_commit_req [JobRequestMoveCommitReq] move spec
109
+ # @param [Hash] opts the optional parameters
110
+ # @option opts [String] :idempotency_key Unique key making retries safe: a repeat send with the same key replays the original response (header Idempotent-Replayed: true) instead of re-running the operation. Reusing a key with a different body returns 422 IDEMPOTENCY_KEY_REUSE.
111
+ # @return [Array<(CommitJobRequestMove200Response, Integer, Hash)>] CommitJobRequestMove200Response data, response status code and response headers
112
+ def commit_job_request_move_with_http_info(id, job_request_move_commit_req, opts = {})
113
+ if @api_client.config.debugging
114
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.commit_job_request_move ...'
115
+ end
116
+ # verify the required parameter 'id' is set
117
+ if @api_client.config.client_side_validation && id.nil?
118
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobRequestBusinessApi.commit_job_request_move"
119
+ end
120
+ # verify the required parameter 'job_request_move_commit_req' is set
121
+ if @api_client.config.client_side_validation && job_request_move_commit_req.nil?
122
+ fail ArgumentError, "Missing the required parameter 'job_request_move_commit_req' when calling JobRequestBusinessApi.commit_job_request_move"
123
+ end
124
+ # resource path
125
+ local_var_path = '/job-requests/{id}/move/commit'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
126
+
127
+ # query parameters
128
+ query_params = opts[:query_params] || {}
129
+
130
+ # header parameters
131
+ header_params = opts[:header_params] || {}
132
+ # HTTP header 'Accept' (if needed)
133
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
134
+ # HTTP header 'Content-Type'
135
+ content_type = @api_client.select_header_content_type(['application/json'])
136
+ if !content_type.nil?
137
+ header_params['Content-Type'] = content_type
138
+ end
139
+ header_params[:'Idempotency-Key'] = opts[:'idempotency_key'] if !opts[:'idempotency_key'].nil?
140
+
141
+ # form parameters
142
+ form_params = opts[:form_params] || {}
143
+
144
+ # http body (model)
145
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_request_move_commit_req)
146
+
147
+ # return_type
148
+ return_type = opts[:debug_return_type] || 'CommitJobRequestMove200Response'
149
+
150
+ # auth_names
151
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
152
+
153
+ new_options = opts.merge(
154
+ :operation => :"JobRequestBusinessApi.commit_job_request_move",
155
+ :header_params => header_params,
156
+ :query_params => query_params,
157
+ :form_params => form_params,
158
+ :body => post_body,
159
+ :auth_names => auth_names,
160
+ :return_type => return_type
161
+ )
162
+
163
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
164
+ if @api_client.config.debugging
165
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#commit_job_request_move\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
166
+ end
167
+ return data, status_code, headers
168
+ end
169
+
170
+ # Confirm a booking on behalf of the customer
171
+ # Fires the customer-actor `confirm_booking` action from the BUSINESS surface (audited as business_on_behalf). Two uses: (1) LIVE — staff confirm a slot for a customer who booked by phone; (2) SANDBOX — the customer magic-token surface is live-only (a sandbox job's link can never reach a real customer), so this is the ONLY way to drive a sandbox test job past booking (book → quote → confirm → assign → complete). Body carries the customer-chosen scheduled_at (business-local naive datetime). DECISION TABLE — every 409 this endpoint returns, and the correct NEXT STEP (branch on error_code, never on the HTTP status): • JOB_REQUEST_STAGE_CONFLICT — the job changed since you read it (NOTE: every FAILED confirm attempt also bumps status_version by design). Next: re-GET the job, retry with the fresh status_version. • JOB_REQUEST_ACTION_NOT_PENDING — the job is no longer at the confirm step (usually: already confirmed). Next: re-GET and show current status; do not retry. • JOB_REQUEST_NO_TECHNICIAN_AVAILABLE — the TIME is infeasible for everyone (outside working hours / the customer window, or nobody qualifies). Next: pick another time via booking-windows / time-segments. NOT an emergency case — displacement cannot conjure capacity. • JOB_REQUEST_TECH_INFEASIBLE — the FORCED technician can never take the job then; `data.reason` says why: cannot_arrive_in_time (commute/shift-start — `data.earliest_feasible_at` (RFC3339 UTC) is the first same-day time they CAN be on site → offer it) | missing_required_skills | not_available_today | not_lead_tier. Next: keep the tech and reschedule to earliest_feasible_at+, OR keep the time and drop technician_id (auto-pick) / choose another tech from time-segments. NOT an emergency case. • JOB_REQUEST_P0_REQUIRES_DISPLACEMENT — the ONLY code that routes to the EMERGENCY flow: the job is P0, the tech qualifies, but the lane is genuinely occupied. Next: POST emergency/candidates → preview → commit (the commit auto-confirms). Caveat: if the occupying jobs are themselves P0 the preview will reject with EMERGENCY_RESCHEDULE_SLOT_OCCUPIED (P0 never displaces P0) — then pick another tech/time.
172
+ # @param id [String] Job request ID
173
+ # @param job_request_confirm_request [JobRequestConfirmRequest] Chosen slot (scheduled_at) + optional technician_id force-assign (P0–P3 flow: pins the ranked candidate, feasibility still enforced)
174
+ # @param [Hash] opts the optional parameters
175
+ # @option opts [String] :idempotency_key Unique key making retries safe: a repeat send with the same key replays the original response (header Idempotent-Replayed: true) instead of re-running the operation. Reusing a key with a different body returns 422 IDEMPOTENCY_KEY_REUSE.
176
+ # @return [ResponseEnvelope]
177
+ def confirm_job_request(id, job_request_confirm_request, opts = {})
178
+ data, _status_code, _headers = confirm_job_request_with_http_info(id, job_request_confirm_request, opts)
179
+ data
180
+ end
181
+
182
+ # Confirm a booking on behalf of the customer
183
+ # Fires the customer-actor &#x60;confirm_booking&#x60; action from the BUSINESS surface (audited as business_on_behalf). Two uses: (1) LIVE — staff confirm a slot for a customer who booked by phone; (2) SANDBOX — the customer magic-token surface is live-only (a sandbox job&#39;s link can never reach a real customer), so this is the ONLY way to drive a sandbox test job past booking (book → quote → confirm → assign → complete). Body carries the customer-chosen scheduled_at (business-local naive datetime). DECISION TABLE — every 409 this endpoint returns, and the correct NEXT STEP (branch on error_code, never on the HTTP status): • JOB_REQUEST_STAGE_CONFLICT — the job changed since you read it (NOTE: every FAILED confirm attempt also bumps status_version by design). Next: re-GET the job, retry with the fresh status_version. • JOB_REQUEST_ACTION_NOT_PENDING — the job is no longer at the confirm step (usually: already confirmed). Next: re-GET and show current status; do not retry. • JOB_REQUEST_NO_TECHNICIAN_AVAILABLE — the TIME is infeasible for everyone (outside working hours / the customer window, or nobody qualifies). Next: pick another time via booking-windows / time-segments. NOT an emergency case — displacement cannot conjure capacity. • JOB_REQUEST_TECH_INFEASIBLE — the FORCED technician can never take the job then; &#x60;data.reason&#x60; says why: cannot_arrive_in_time (commute/shift-start — &#x60;data.earliest_feasible_at&#x60; (RFC3339 UTC) is the first same-day time they CAN be on site → offer it) | missing_required_skills | not_available_today | not_lead_tier. Next: keep the tech and reschedule to earliest_feasible_at+, OR keep the time and drop technician_id (auto-pick) / choose another tech from time-segments. NOT an emergency case. • JOB_REQUEST_P0_REQUIRES_DISPLACEMENT — the ONLY code that routes to the EMERGENCY flow: the job is P0, the tech qualifies, but the lane is genuinely occupied. Next: POST emergency/candidates → preview → commit (the commit auto-confirms). Caveat: if the occupying jobs are themselves P0 the preview will reject with EMERGENCY_RESCHEDULE_SLOT_OCCUPIED (P0 never displaces P0) — then pick another tech/time.
184
+ # @param id [String] Job request ID
185
+ # @param job_request_confirm_request [JobRequestConfirmRequest] Chosen slot (scheduled_at) + optional technician_id force-assign (P0–P3 flow: pins the ranked candidate, feasibility still enforced)
186
+ # @param [Hash] opts the optional parameters
187
+ # @option opts [String] :idempotency_key Unique key making retries safe: a repeat send with the same key replays the original response (header Idempotent-Replayed: true) instead of re-running the operation. Reusing a key with a different body returns 422 IDEMPOTENCY_KEY_REUSE.
188
+ # @return [Array<(ResponseEnvelope, Integer, Hash)>] ResponseEnvelope data, response status code and response headers
189
+ def confirm_job_request_with_http_info(id, job_request_confirm_request, opts = {})
190
+ if @api_client.config.debugging
191
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.confirm_job_request ...'
192
+ end
193
+ # verify the required parameter 'id' is set
194
+ if @api_client.config.client_side_validation && id.nil?
195
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobRequestBusinessApi.confirm_job_request"
196
+ end
197
+ # verify the required parameter 'job_request_confirm_request' is set
198
+ if @api_client.config.client_side_validation && job_request_confirm_request.nil?
199
+ fail ArgumentError, "Missing the required parameter 'job_request_confirm_request' when calling JobRequestBusinessApi.confirm_job_request"
200
+ end
201
+ # resource path
202
+ local_var_path = '/job-requests/{id}/confirm'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
203
+
204
+ # query parameters
205
+ query_params = opts[:query_params] || {}
206
+
207
+ # header parameters
208
+ header_params = opts[:header_params] || {}
209
+ # HTTP header 'Accept' (if needed)
210
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
211
+ # HTTP header 'Content-Type'
212
+ content_type = @api_client.select_header_content_type(['application/json'])
213
+ if !content_type.nil?
214
+ header_params['Content-Type'] = content_type
215
+ end
216
+ header_params[:'Idempotency-Key'] = opts[:'idempotency_key'] if !opts[:'idempotency_key'].nil?
217
+
218
+ # form parameters
219
+ form_params = opts[:form_params] || {}
220
+
221
+ # http body (model)
222
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_request_confirm_request)
223
+
224
+ # return_type
225
+ return_type = opts[:debug_return_type] || 'ResponseEnvelope'
226
+
227
+ # auth_names
228
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
229
+
230
+ new_options = opts.merge(
231
+ :operation => :"JobRequestBusinessApi.confirm_job_request",
232
+ :header_params => header_params,
233
+ :query_params => query_params,
234
+ :form_params => form_params,
235
+ :body => post_body,
236
+ :auth_names => auth_names,
237
+ :return_type => return_type
238
+ )
239
+
240
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
241
+ if @api_client.config.debugging
242
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#confirm_job_request\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
243
+ end
244
+ return data, status_code, headers
245
+ end
246
+
247
+ # Create a job request
248
+ # Books a field-service job — the work order that enters the dispatch & scheduling pipeline. Send the customer's UUID plus requested `job_dates` (date + morning/afternoon/evening periods, ideally offered from GET /job-requests/booking-windows), optional `job_type_id` (service catalog), `skill_ids` (required technician qualifications) and a free-text description. Quoting, technician/crew assignment and completion then advance the work order through the business's workflow.
23
249
  # @param job_request_create_request [JobRequestCreateRequest] Booking payload
24
250
  # @param [Hash] opts the optional parameters
25
251
  # @option opts [String] :x_timezone Customer IANA timezone
252
+ # @option opts [String] :idempotency_key Unique key making retries safe: a repeat send with the same key returns the original booking instead of creating a duplicate
26
253
  # @return [CreateJobRequest200Response]
27
254
  def create_job_request(job_request_create_request, opts = {})
28
255
  data, _status_code, _headers = create_job_request_with_http_info(job_request_create_request, opts)
29
256
  data
30
257
  end
31
258
 
32
- # Create a job request (business actor)
259
+ # Create a job request
260
+ # Books a field-service job — the work order that enters the dispatch &amp; scheduling pipeline. Send the customer&#39;s UUID plus requested &#x60;job_dates&#x60; (date + morning/afternoon/evening periods, ideally offered from GET /job-requests/booking-windows), optional &#x60;job_type_id&#x60; (service catalog), &#x60;skill_ids&#x60; (required technician qualifications) and a free-text description. Quoting, technician/crew assignment and completion then advance the work order through the business&#39;s workflow.
33
261
  # @param job_request_create_request [JobRequestCreateRequest] Booking payload
34
262
  # @param [Hash] opts the optional parameters
35
263
  # @option opts [String] :x_timezone Customer IANA timezone
264
+ # @option opts [String] :idempotency_key Unique key making retries safe: a repeat send with the same key returns the original booking instead of creating a duplicate
36
265
  # @return [Array<(CreateJobRequest200Response, Integer, Hash)>] CreateJobRequest200Response data, response status code and response headers
37
266
  def create_job_request_with_http_info(job_request_create_request, opts = {})
38
267
  if @api_client.config.debugging
@@ -58,6 +287,7 @@ module Crisphive
58
287
  header_params['Content-Type'] = content_type
59
288
  end
60
289
  header_params[:'X-Timezone'] = opts[:'x_timezone'] if !opts[:'x_timezone'].nil?
290
+ header_params[:'Idempotency-Key'] = opts[:'idempotency_key'] if !opts[:'idempotency_key'].nil?
61
291
 
62
292
  # form parameters
63
293
  form_params = opts[:form_params] || {}
@@ -89,6 +319,7 @@ module Crisphive
89
319
  end
90
320
 
91
321
  # Get a job request
322
+ # Returns the full work order: current workflow status, quoted duration, confirmed schedule, customer contact snapshot and the assigned technician / crew — everything a dispatcher or an external field-service system needs to track one job.
92
323
  # @param id [String] Job request ID (UUID or short_code)
93
324
  # @param [Hash] opts the optional parameters
94
325
  # @return [GetJobRequest200Response]
@@ -98,6 +329,7 @@ module Crisphive
98
329
  end
99
330
 
100
331
  # Get a job request
332
+ # Returns the full work order: current workflow status, quoted duration, confirmed schedule, customer contact snapshot and the assigned technician / crew — everything a dispatcher or an external field-service system needs to track one job.
101
333
  # @param id [String] Job request ID (UUID or short_code)
102
334
  # @param [Hash] opts the optional parameters
103
335
  # @return [Array<(GetJobRequest200Response, Integer, Hash)>] GetJobRequest200Response data, response status code and response headers
@@ -149,8 +381,8 @@ module Crisphive
149
381
  return data, status_code, headers
150
382
  end
151
383
 
152
- # Job timeline (business surface — also serves tech via BusinessAuth)
153
- # Per-status events[] composed from workflow snapshot + scattered typed cols + action_audit. FE renders as the Job Timeline panel (completed step = filled check, current = outline ring, upcoming = empty). entered_at nil for upcoming steps + older jobs missing the typed-col backfill.
384
+ # Job timeline
385
+ # Per-status progress of a job's lifecycle (e.g. booked confirmed on the way arrived → completed, following the business's configured workflow) — render it as a job-tracking timeline. Each status carries its state (completed | current | upcoming), when the job entered it, and the actions fired within it. entered_at may be null for upcoming steps and for older jobs predating the backfill.
154
386
  # @param id [String] Job request ID (UUID or short_code)
155
387
  # @param [Hash] opts the optional parameters
156
388
  # @return [GetJobRequestTimeline200Response]
@@ -159,8 +391,8 @@ module Crisphive
159
391
  data
160
392
  end
161
393
 
162
- # Job timeline (business surface — also serves tech via BusinessAuth)
163
- # Per-status events[] composed from workflow snapshot + scattered typed cols + action_audit. FE renders as the Job Timeline panel (completed step &#x3D; filled check, current &#x3D; outline ring, upcoming &#x3D; empty). entered_at nil for upcoming steps + older jobs missing the typed-col backfill.
394
+ # Job timeline
395
+ # Per-status progress of a job&#39;s lifecycle (e.g. booked confirmed on the way arrived → completed, following the business&#39;s configured workflow) — render it as a job-tracking timeline. Each status carries its state (completed | current | upcoming), when the job entered it, and the actions fired within it. entered_at may be null for upcoming steps and for older jobs predating the backfill.
164
396
  # @param id [String] Job request ID (UUID or short_code)
165
397
  # @param [Hash] opts the optional parameters
166
398
  # @return [Array<(GetJobRequestTimeline200Response, Integer, Hash)>] GetJobRequestTimeline200Response data, response status code and response headers
@@ -212,33 +444,35 @@ module Crisphive
212
444
  return data, status_code, headers
213
445
  end
214
446
 
215
- # Booking availability (business actor)
216
- # @param x_timezone [String] Customer IANA timezone
447
+ # One technician's real schedule (sessions + time off)
448
+ # The technician's ACTUAL occupancy over a date range: every job session on their lane (solo/lead and crew) plus approved time-off blocks. Weekly recurring working hours come from the technician-availability endpoints — combine both for the full availability picture (\"get crew availability\"). from/to are business-local dates (YYYY-MM-DD, inclusive); omitted = today .. +7 days; range max 31 days.
449
+ # @param id [String] Technician ID
217
450
  # @param [Hash] opts the optional parameters
218
- # @option opts [String] :from Start YYYY-MM-DD
219
- # @option opts [String] :to End YYYY-MM-DD
220
- # @return [ListJobRequestBookingWindows200Response]
221
- def list_job_request_booking_windows(x_timezone, opts = {})
222
- data, _status_code, _headers = list_job_request_booking_windows_with_http_info(x_timezone, opts)
451
+ # @option opts [String] :from Start date (YYYY-MM-DD, business-local; default today)
452
+ # @option opts [String] :to End date (YYYY-MM-DD, inclusive; default from+7d; max range 31 days)
453
+ # @return [GetTechnicianSchedule200Response]
454
+ def get_technician_schedule(id, opts = {})
455
+ data, _status_code, _headers = get_technician_schedule_with_http_info(id, opts)
223
456
  data
224
457
  end
225
458
 
226
- # Booking availability (business actor)
227
- # @param x_timezone [String] Customer IANA timezone
459
+ # One technician&#39;s real schedule (sessions + time off)
460
+ # The technician&#39;s ACTUAL occupancy over a date range: every job session on their lane (solo/lead and crew) plus approved time-off blocks. Weekly recurring working hours come from the technician-availability endpoints — combine both for the full availability picture (\&quot;get crew availability\&quot;). from/to are business-local dates (YYYY-MM-DD, inclusive); omitted &#x3D; today .. +7 days; range max 31 days.
461
+ # @param id [String] Technician ID
228
462
  # @param [Hash] opts the optional parameters
229
- # @option opts [String] :from Start YYYY-MM-DD
230
- # @option opts [String] :to End YYYY-MM-DD
231
- # @return [Array<(ListJobRequestBookingWindows200Response, Integer, Hash)>] ListJobRequestBookingWindows200Response data, response status code and response headers
232
- def list_job_request_booking_windows_with_http_info(x_timezone, opts = {})
463
+ # @option opts [String] :from Start date (YYYY-MM-DD, business-local; default today)
464
+ # @option opts [String] :to End date (YYYY-MM-DD, inclusive; default from+7d; max range 31 days)
465
+ # @return [Array<(GetTechnicianSchedule200Response, Integer, Hash)>] GetTechnicianSchedule200Response data, response status code and response headers
466
+ def get_technician_schedule_with_http_info(id, opts = {})
233
467
  if @api_client.config.debugging
234
- @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_job_request_booking_windows ...'
468
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.get_technician_schedule ...'
235
469
  end
236
- # verify the required parameter 'x_timezone' is set
237
- if @api_client.config.client_side_validation && x_timezone.nil?
238
- fail ArgumentError, "Missing the required parameter 'x_timezone' when calling JobRequestBusinessApi.list_job_request_booking_windows"
470
+ # verify the required parameter 'id' is set
471
+ if @api_client.config.client_side_validation && id.nil?
472
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobRequestBusinessApi.get_technician_schedule"
239
473
  end
240
474
  # resource path
241
- local_var_path = '/job-requests/booking-windows'
475
+ local_var_path = '/technicians/{id}/schedule'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
242
476
 
243
477
  # query parameters
244
478
  query_params = opts[:query_params] || {}
@@ -249,7 +483,6 @@ module Crisphive
249
483
  header_params = opts[:header_params] || {}
250
484
  # HTTP header 'Accept' (if needed)
251
485
  header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
252
- header_params[:'X-Timezone'] = x_timezone
253
486
 
254
487
  # form parameters
255
488
  form_params = opts[:form_params] || {}
@@ -258,13 +491,13 @@ module Crisphive
258
491
  post_body = opts[:debug_body]
259
492
 
260
493
  # return_type
261
- return_type = opts[:debug_return_type] || 'ListJobRequestBookingWindows200Response'
494
+ return_type = opts[:debug_return_type] || 'GetTechnicianSchedule200Response'
262
495
 
263
496
  # auth_names
264
497
  auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
265
498
 
266
499
  new_options = opts.merge(
267
- :operation => :"JobRequestBusinessApi.list_job_request_booking_windows",
500
+ :operation => :"JobRequestBusinessApi.get_technician_schedule",
268
501
  :header_params => header_params,
269
502
  :query_params => query_params,
270
503
  :form_params => form_params,
@@ -275,57 +508,48 @@ module Crisphive
275
508
 
276
509
  data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
277
510
  if @api_client.config.debugging
278
- @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_job_request_booking_windows\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
511
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#get_technician_schedule\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
279
512
  end
280
513
  return data, status_code, headers
281
514
  end
282
515
 
283
- # Poll for new & changed job requests (sync feed)
284
- # Keep your own copy of bookings in sync WITHOUT re-listing everything: returns the job requests whose state changed (created, status transition, reschedule, soft-delete/archive) at or after the `since` cursor, ordered oldest-change-first (updated_at ASC). How to use it: (1) On your first poll OMIT `since` — the server primes the cursor at \"now\", returns no items and a `next_since`. (2) Store `next_since` and pass it as `since` on the next poll. (3) Apply each returned item to your store by UPSERTING on `id` (the server re-scans a ~5s safety window, so the same job may appear again never blindly append). (4) If `has_more` is true the page filled to `limit` and more changes are already waiting — poll again immediately; otherwise wait your normal interval (e.g. 5–15s). This is NOT pagination it is a time-keyed change feed. Use the paginated GET /job-requests for the initial bulk load, then this endpoint to stay live. Filters (status_keys, customer_id, …) narrow the feed to the slice you care about.
516
+ # Matching crew candidates for a job
517
+ # Technicians who can actually take this job, matched and ranked by the smart-assignment engine skills per crew slot, weekly availability, existing schedule, time off and travel are all checked; each candidate carries a score breakdown (distance, travel, matched skills) plus the exact on-site session plan they would work. NOT a raw roster list (use GET /technicians for that). Returns the ranked feasible LEAD pool by default; pass include_buddies=true to also return per-slot buddy pools, include_vehicle=true to include the available-vehicle list. force_lead_id checks one specific technician: returns only that lead (with their crew combo) if feasible, else 409 JOB_REQUEST_NO_TECHNICIAN_AVAILABLE.
518
+ # @param id [String] Job request ID or short_code
285
519
  # @param [Hash] opts the optional parameters
286
- # @option opts [String] :status_keys Comma-separated status slugs only surface changes to jobs in these statuses
287
- # @option opts [String] :priority Priority filter (normal|emergency)
288
- # @option opts [String] :customer_id Only changes to this customer&#39;s jobs (UUID)
289
- # @option opts [String] :technician_id Only changes to jobs assigned to this technician (UUID)
290
- # @option opts [String] :scheduled_from Filter from (YYYY-MM-DD or RFC3339); range is [from, to)
291
- # @option opts [String] :scheduled_to Filter to (YYYY-MM-DD or RFC3339), exclusive
292
- # @option opts [String] :since RFC3339 cursor from the prior response&#39;s next_since. OMIT on the first poll to prime the cursor at server-now.
293
- # @option opts [Integer] :limit Max changes per poll (default 15, max 1000). If the page fills, has_more&#x3D;true.
294
- # @return [ListJobRequestChanges200Response]
295
- def list_job_request_changes(opts = {})
296
- data, _status_code, _headers = list_job_request_changes_with_http_info(opts)
520
+ # @option opts [Boolean] :include_buddies Also return buddy candidate pools
521
+ # @option opts [Boolean] :include_vehicle Also return the available-vehicle list
522
+ # @option opts [String] :force_lead_id Check a specific technician as lead — returns only that lead if feasible, else 409
523
+ # @return [ListCrewCandidates200Response]
524
+ def list_crew_candidates(id, opts = {})
525
+ data, _status_code, _headers = list_crew_candidates_with_http_info(id, opts)
297
526
  data
298
527
  end
299
528
 
300
- # Poll for new &amp; changed job requests (sync feed)
301
- # Keep your own copy of bookings in sync WITHOUT re-listing everything: returns the job requests whose state changed (created, status transition, reschedule, soft-delete/archive) at or after the &#x60;since&#x60; cursor, ordered oldest-change-first (updated_at ASC). How to use it: (1) On your first poll OMIT &#x60;since&#x60; the server primes the cursor at \&quot;now\&quot;, returns no items and a &#x60;next_since&#x60;. (2) Store &#x60;next_since&#x60; and pass it as &#x60;since&#x60; on the next poll. (3) Apply each returned item to your store by UPSERTING on &#x60;id&#x60; (the server re-scans a ~5s safety window, so the same job may appear again never blindly append). (4) If &#x60;has_more&#x60; is true the page filled to &#x60;limit&#x60; and more changes are already waiting — poll again immediately; otherwise wait your normal interval (e.g. 5–15s). This is NOT pagination — it is a time-keyed change feed. Use the paginated GET /job-requests for the initial bulk load, then this endpoint to stay live. Filters (status_keys, customer_id, …) narrow the feed to the slice you care about.
529
+ # Matching crew candidates for a job
530
+ # Technicians who can actually take this job, matched and ranked by the smart-assignment engineskills per crew slot, weekly availability, existing schedule, time off and travel are all checked; each candidate carries a score breakdown (distance, travel, matched skills) plus the exact on-site session plan they would work. NOT a raw roster list (use GET /technicians for that). Returns the ranked feasible LEAD pool by default; pass include_buddies&#x3D;true to also return per-slot buddy pools, include_vehicle&#x3D;true to include the available-vehicle list. force_lead_id checks one specific technician: returns only that lead (with their crew combo) if feasible, else 409 JOB_REQUEST_NO_TECHNICIAN_AVAILABLE.
531
+ # @param id [String] Job request ID or short_code
302
532
  # @param [Hash] opts the optional parameters
303
- # @option opts [String] :status_keys Comma-separated status slugs only surface changes to jobs in these statuses
304
- # @option opts [String] :priority Priority filter (normal|emergency)
305
- # @option opts [String] :customer_id Only changes to this customer&#39;s jobs (UUID)
306
- # @option opts [String] :technician_id Only changes to jobs assigned to this technician (UUID)
307
- # @option opts [String] :scheduled_from Filter from (YYYY-MM-DD or RFC3339); range is [from, to)
308
- # @option opts [String] :scheduled_to Filter to (YYYY-MM-DD or RFC3339), exclusive
309
- # @option opts [String] :since RFC3339 cursor from the prior response&#39;s next_since. OMIT on the first poll to prime the cursor at server-now.
310
- # @option opts [Integer] :limit Max changes per poll (default 15, max 1000). If the page fills, has_more&#x3D;true.
311
- # @return [Array<(ListJobRequestChanges200Response, Integer, Hash)>] ListJobRequestChanges200Response data, response status code and response headers
312
- def list_job_request_changes_with_http_info(opts = {})
533
+ # @option opts [Boolean] :include_buddies Also return buddy candidate pools
534
+ # @option opts [Boolean] :include_vehicle Also return the available-vehicle list
535
+ # @option opts [String] :force_lead_id Check a specific technician as lead — returns only that lead if feasible, else 409
536
+ # @return [Array<(ListCrewCandidates200Response, Integer, Hash)>] ListCrewCandidates200Response data, response status code and response headers
537
+ def list_crew_candidates_with_http_info(id, opts = {})
313
538
  if @api_client.config.debugging
314
- @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_job_request_changes ...'
539
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_crew_candidates ...'
540
+ end
541
+ # verify the required parameter 'id' is set
542
+ if @api_client.config.client_side_validation && id.nil?
543
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobRequestBusinessApi.list_crew_candidates"
315
544
  end
316
545
  # resource path
317
- local_var_path = '/job-requests/changes'
546
+ local_var_path = '/job-requests/{id}/crew-candidates'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
318
547
 
319
548
  # query parameters
320
549
  query_params = opts[:query_params] || {}
321
- query_params[:'status_keys'] = opts[:'status_keys'] if !opts[:'status_keys'].nil?
322
- query_params[:'priority'] = opts[:'priority'] if !opts[:'priority'].nil?
323
- query_params[:'customer_id'] = opts[:'customer_id'] if !opts[:'customer_id'].nil?
324
- query_params[:'technician_id'] = opts[:'technician_id'] if !opts[:'technician_id'].nil?
325
- query_params[:'scheduled_from'] = opts[:'scheduled_from'] if !opts[:'scheduled_from'].nil?
326
- query_params[:'scheduled_to'] = opts[:'scheduled_to'] if !opts[:'scheduled_to'].nil?
327
- query_params[:'since'] = opts[:'since'] if !opts[:'since'].nil?
328
- query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
550
+ query_params[:'include_buddies'] = opts[:'include_buddies'] if !opts[:'include_buddies'].nil?
551
+ query_params[:'include_vehicle'] = opts[:'include_vehicle'] if !opts[:'include_vehicle'].nil?
552
+ query_params[:'force_lead_id'] = opts[:'force_lead_id'] if !opts[:'force_lead_id'].nil?
329
553
 
330
554
  # header parameters
331
555
  header_params = opts[:header_params] || {}
@@ -339,13 +563,13 @@ module Crisphive
339
563
  post_body = opts[:debug_body]
340
564
 
341
565
  # return_type
342
- return_type = opts[:debug_return_type] || 'ListJobRequestChanges200Response'
566
+ return_type = opts[:debug_return_type] || 'ListCrewCandidates200Response'
343
567
 
344
568
  # auth_names
345
569
  auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
346
570
 
347
571
  new_options = opts.merge(
348
- :operation => :"JobRequestBusinessApi.list_job_request_changes",
572
+ :operation => :"JobRequestBusinessApi.list_crew_candidates",
349
573
  :header_params => header_params,
350
574
  :query_params => query_params,
351
575
  :form_params => form_params,
@@ -356,81 +580,64 @@ module Crisphive
356
580
 
357
581
  data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
358
582
  if @api_client.config.debugging
359
- @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_job_request_changes\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
583
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_crew_candidates\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
360
584
  end
361
585
  return data, status_code, headers
362
586
  end
363
587
 
364
- # List job requests
588
+ # Rank technicians for a P0 emergency insert
589
+ # Returns the technicians who could take the emergency job at the requested start, ranked FASTEST-ARRIVAL first (arrival beats route efficiency for a P0). The response also carries a historical `crew_recommendation` (median crew size on comparable completed jobs + mandatory disclaimer — AC-2). Booked technicians are still candidates — each entry carries the displacement preview (which lower-priority jobs would be pushed, per day) that committing to them would cause; total_moves=0 means a free slot. P0 jobs are never displaced; P1 only by a P0. ETA is estimated from the technician's start location (no live GPS). Feed the chosen technician_id into emergency/preview + emergency/commit. 409 NEXT STEPS: EMERGENCY_RESCHEDULE_NOT_ELIGIBLE — the job cannot be emergency-inserted (not P0, already started/completed/archived, or not quoted): fix the job state or use a normal confirm. EMERGENCY_RESCHEDULE_CREW_UNSUPPORTED — crew jobs cannot use the emergency flow (v1): staff via confirm/reassign instead. EMERGENCY_RESCHEDULE_MULTIDAY_UNSUPPORTED — a confirmed multi-day job cannot be re-inserted (v1): use the normal reassign flow. EMERGENCY_RESCHEDULE_NO_WORKING_DAY — the chosen date has no working hours: pick a working day. EMERGENCY_RESCHEDULE_IN_PAST — start time already passed: pick a future time.
590
+ # @param job_request_emergency_candidates_request [JobRequestEmergencyCandidatesRequest] Emergency job + desired start
365
591
  # @param [Hash] opts the optional parameters
366
- # @option opts [String] :status_keys Comma-separated status slugs
367
- # @option opts [String] :status active (default) | archived | all
368
- # @option opts [String] :customer_id Customer UUID
369
- # @option opts [String] :technician_id Technician UUID
370
- # @option opts [String] :scheduled_from Filter from (YYYY-MM-DD or RFC3339); range is [from, to)
371
- # @option opts [String] :scheduled_to Filter to (YYYY-MM-DD or RFC3339), exclusive
372
- # @option opts [String] :q Search short_code or description (case-insensitive, partial match)
373
- # @option opts [String] :sort Sort key
374
- # @option opts [Integer] :page Page number
375
- # @option opts [Integer] :limit Page size
376
- # @return [ListJobRequests200Response]
377
- def list_job_requests(opts = {})
378
- data, _status_code, _headers = list_job_requests_with_http_info(opts)
592
+ # @return [ListEmergencyCandidates200Response]
593
+ def list_emergency_candidates(job_request_emergency_candidates_request, opts = {})
594
+ data, _status_code, _headers = list_emergency_candidates_with_http_info(job_request_emergency_candidates_request, opts)
379
595
  data
380
596
  end
381
597
 
382
- # List job requests
598
+ # Rank technicians for a P0 emergency insert
599
+ # Returns the technicians who could take the emergency job at the requested start, ranked FASTEST-ARRIVAL first (arrival beats route efficiency for a P0). The response also carries a historical &#x60;crew_recommendation&#x60; (median crew size on comparable completed jobs + mandatory disclaimer — AC-2). Booked technicians are still candidates — each entry carries the displacement preview (which lower-priority jobs would be pushed, per day) that committing to them would cause; total_moves&#x3D;0 means a free slot. P0 jobs are never displaced; P1 only by a P0. ETA is estimated from the technician&#39;s start location (no live GPS). Feed the chosen technician_id into emergency/preview + emergency/commit. 409 NEXT STEPS: EMERGENCY_RESCHEDULE_NOT_ELIGIBLE — the job cannot be emergency-inserted (not P0, already started/completed/archived, or not quoted): fix the job state or use a normal confirm. EMERGENCY_RESCHEDULE_CREW_UNSUPPORTED — crew jobs cannot use the emergency flow (v1): staff via confirm/reassign instead. EMERGENCY_RESCHEDULE_MULTIDAY_UNSUPPORTED — a confirmed multi-day job cannot be re-inserted (v1): use the normal reassign flow. EMERGENCY_RESCHEDULE_NO_WORKING_DAY — the chosen date has no working hours: pick a working day. EMERGENCY_RESCHEDULE_IN_PAST — start time already passed: pick a future time.
600
+ # @param job_request_emergency_candidates_request [JobRequestEmergencyCandidatesRequest] Emergency job + desired start
383
601
  # @param [Hash] opts the optional parameters
384
- # @option opts [String] :status_keys Comma-separated status slugs
385
- # @option opts [String] :status active (default) | archived | all
386
- # @option opts [String] :customer_id Customer UUID
387
- # @option opts [String] :technician_id Technician UUID
388
- # @option opts [String] :scheduled_from Filter from (YYYY-MM-DD or RFC3339); range is [from, to)
389
- # @option opts [String] :scheduled_to Filter to (YYYY-MM-DD or RFC3339), exclusive
390
- # @option opts [String] :q Search short_code or description (case-insensitive, partial match)
391
- # @option opts [String] :sort Sort key
392
- # @option opts [Integer] :page Page number
393
- # @option opts [Integer] :limit Page size
394
- # @return [Array<(ListJobRequests200Response, Integer, Hash)>] ListJobRequests200Response data, response status code and response headers
395
- def list_job_requests_with_http_info(opts = {})
602
+ # @return [Array<(ListEmergencyCandidates200Response, Integer, Hash)>] ListEmergencyCandidates200Response data, response status code and response headers
603
+ def list_emergency_candidates_with_http_info(job_request_emergency_candidates_request, opts = {})
396
604
  if @api_client.config.debugging
397
- @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_job_requests ...'
605
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_emergency_candidates ...'
606
+ end
607
+ # verify the required parameter 'job_request_emergency_candidates_request' is set
608
+ if @api_client.config.client_side_validation && job_request_emergency_candidates_request.nil?
609
+ fail ArgumentError, "Missing the required parameter 'job_request_emergency_candidates_request' when calling JobRequestBusinessApi.list_emergency_candidates"
398
610
  end
399
611
  # resource path
400
- local_var_path = '/job-requests'
612
+ local_var_path = '/job-requests/emergency/candidates'
401
613
 
402
614
  # query parameters
403
615
  query_params = opts[:query_params] || {}
404
- query_params[:'status_keys'] = opts[:'status_keys'] if !opts[:'status_keys'].nil?
405
- query_params[:'status'] = opts[:'status'] if !opts[:'status'].nil?
406
- query_params[:'customer_id'] = opts[:'customer_id'] if !opts[:'customer_id'].nil?
407
- query_params[:'technician_id'] = opts[:'technician_id'] if !opts[:'technician_id'].nil?
408
- query_params[:'scheduled_from'] = opts[:'scheduled_from'] if !opts[:'scheduled_from'].nil?
409
- query_params[:'scheduled_to'] = opts[:'scheduled_to'] if !opts[:'scheduled_to'].nil?
410
- query_params[:'q'] = opts[:'q'] if !opts[:'q'].nil?
411
- query_params[:'sort'] = opts[:'sort'] if !opts[:'sort'].nil?
412
- query_params[:'page'] = opts[:'page'] if !opts[:'page'].nil?
413
- query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
414
616
 
415
617
  # header parameters
416
618
  header_params = opts[:header_params] || {}
417
619
  # HTTP header 'Accept' (if needed)
418
620
  header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
621
+ # HTTP header 'Content-Type'
622
+ content_type = @api_client.select_header_content_type(['application/json'])
623
+ if !content_type.nil?
624
+ header_params['Content-Type'] = content_type
625
+ end
419
626
 
420
627
  # form parameters
421
628
  form_params = opts[:form_params] || {}
422
629
 
423
630
  # http body (model)
424
- post_body = opts[:debug_body]
631
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_request_emergency_candidates_request)
425
632
 
426
633
  # return_type
427
- return_type = opts[:debug_return_type] || 'ListJobRequests200Response'
634
+ return_type = opts[:debug_return_type] || 'ListEmergencyCandidates200Response'
428
635
 
429
636
  # auth_names
430
637
  auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
431
638
 
432
639
  new_options = opts.merge(
433
- :operation => :"JobRequestBusinessApi.list_job_requests",
640
+ :operation => :"JobRequestBusinessApi.list_emergency_candidates",
434
641
  :header_params => header_params,
435
642
  :query_params => query_params,
436
643
  :form_params => form_params,
@@ -439,9 +646,692 @@ module Crisphive
439
646
  :return_type => return_type
440
647
  )
441
648
 
442
- data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
649
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
443
650
  if @api_client.config.debugging
444
- @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_job_requests\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
651
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_emergency_candidates\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
652
+ end
653
+ return data, status_code, headers
654
+ end
655
+
656
+ # Booking availability
657
+ # Real-time appointment availability from the scheduling engine: returns the bookable date + time-period windows given technician capacity, working hours and service-territory coverage. Call this before creating a job request and offer the customer ONLY the returned windows — it prevents unschedulable bookings.
658
+ # @param x_timezone [String] Customer IANA timezone
659
+ # @param [Hash] opts the optional parameters
660
+ # @option opts [String] :from Start YYYY-MM-DD
661
+ # @option opts [String] :to End YYYY-MM-DD
662
+ # @return [ListJobRequestBookingWindows200Response]
663
+ def list_job_request_booking_windows(x_timezone, opts = {})
664
+ data, _status_code, _headers = list_job_request_booking_windows_with_http_info(x_timezone, opts)
665
+ data
666
+ end
667
+
668
+ # Booking availability
669
+ # Real-time appointment availability from the scheduling engine: returns the bookable date + time-period windows given technician capacity, working hours and service-territory coverage. Call this before creating a job request and offer the customer ONLY the returned windows — it prevents unschedulable bookings.
670
+ # @param x_timezone [String] Customer IANA timezone
671
+ # @param [Hash] opts the optional parameters
672
+ # @option opts [String] :from Start YYYY-MM-DD
673
+ # @option opts [String] :to End YYYY-MM-DD
674
+ # @return [Array<(ListJobRequestBookingWindows200Response, Integer, Hash)>] ListJobRequestBookingWindows200Response data, response status code and response headers
675
+ def list_job_request_booking_windows_with_http_info(x_timezone, opts = {})
676
+ if @api_client.config.debugging
677
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_job_request_booking_windows ...'
678
+ end
679
+ # verify the required parameter 'x_timezone' is set
680
+ if @api_client.config.client_side_validation && x_timezone.nil?
681
+ fail ArgumentError, "Missing the required parameter 'x_timezone' when calling JobRequestBusinessApi.list_job_request_booking_windows"
682
+ end
683
+ # resource path
684
+ local_var_path = '/job-requests/booking-windows'
685
+
686
+ # query parameters
687
+ query_params = opts[:query_params] || {}
688
+ query_params[:'from'] = opts[:'from'] if !opts[:'from'].nil?
689
+ query_params[:'to'] = opts[:'to'] if !opts[:'to'].nil?
690
+
691
+ # header parameters
692
+ header_params = opts[:header_params] || {}
693
+ # HTTP header 'Accept' (if needed)
694
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
695
+ header_params[:'X-Timezone'] = x_timezone
696
+
697
+ # form parameters
698
+ form_params = opts[:form_params] || {}
699
+
700
+ # http body (model)
701
+ post_body = opts[:debug_body]
702
+
703
+ # return_type
704
+ return_type = opts[:debug_return_type] || 'ListJobRequestBookingWindows200Response'
705
+
706
+ # auth_names
707
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
708
+
709
+ new_options = opts.merge(
710
+ :operation => :"JobRequestBusinessApi.list_job_request_booking_windows",
711
+ :header_params => header_params,
712
+ :query_params => query_params,
713
+ :form_params => form_params,
714
+ :body => post_body,
715
+ :auth_names => auth_names,
716
+ :return_type => return_type
717
+ )
718
+
719
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
720
+ if @api_client.config.debugging
721
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_job_request_booking_windows\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
722
+ end
723
+ return data, status_code, headers
724
+ end
725
+
726
+ # Poll for new & changed job requests (sync feed)
727
+ # Keep an external system (your CRM, ERP or field-service tool) in sync with bookings WITHOUT re-listing everything: returns the job requests (work orders) whose state changed (created, status transition, reschedule, soft-delete/archive) at or after the `since` cursor, ordered oldest-change-first (updated_at ASC). How to use it: (1) On your first poll OMIT `since` — the server primes the cursor at \"now\", returns no items and a `next_since`. (2) Store `next_since` and pass it as `since` on the next poll. (3) Apply each returned item to your store by UPSERTING on `id` (the server re-scans a ~5s safety window, so the same job may appear again — never blindly append). (4) If `has_more` is true the page filled to `limit` and more changes are already waiting — poll again immediately; otherwise wait your normal interval (e.g. 5–15s). This is NOT pagination — it is a time-keyed change feed. Use the paginated GET /job-requests for the initial bulk load, then this endpoint to stay live. Filters (status_keys, customer_id, …) narrow the feed to the slice you care about.
728
+ # @param [Hash] opts the optional parameters
729
+ # @option opts [String] :status_keys Comma-separated status slugs — only surface changes to jobs in these statuses
730
+ # @option opts [String] :priority Priority filter (p0|p1|p2|p3)
731
+ # @option opts [String] :customer_id Only changes to this customer&#39;s jobs (UUID)
732
+ # @option opts [String] :technician_id Only changes to jobs assigned to this technician (UUID)
733
+ # @option opts [String] :scheduled_from Filter from (YYYY-MM-DD &#x3D; start of that day in the business timezone, or RFC3339); range is [from, to)
734
+ # @option opts [String] :scheduled_to Filter to (YYYY-MM-DD &#x3D; end of that day in the business timezone, or RFC3339), exclusive
735
+ # @option opts [String] :since RFC3339 cursor from the prior response&#39;s next_since. OMIT on the first poll to prime the cursor at server-now.
736
+ # @option opts [Integer] :limit Max changes per poll (default 15, max 1000). If the page fills, has_more&#x3D;true.
737
+ # @return [ListJobRequestChanges200Response]
738
+ def list_job_request_changes(opts = {})
739
+ data, _status_code, _headers = list_job_request_changes_with_http_info(opts)
740
+ data
741
+ end
742
+
743
+ # Poll for new &amp; changed job requests (sync feed)
744
+ # Keep an external system (your CRM, ERP or field-service tool) in sync with bookings WITHOUT re-listing everything: returns the job requests (work orders) whose state changed (created, status transition, reschedule, soft-delete/archive) at or after the &#x60;since&#x60; cursor, ordered oldest-change-first (updated_at ASC). How to use it: (1) On your first poll OMIT &#x60;since&#x60; — the server primes the cursor at \&quot;now\&quot;, returns no items and a &#x60;next_since&#x60;. (2) Store &#x60;next_since&#x60; and pass it as &#x60;since&#x60; on the next poll. (3) Apply each returned item to your store by UPSERTING on &#x60;id&#x60; (the server re-scans a ~5s safety window, so the same job may appear again — never blindly append). (4) If &#x60;has_more&#x60; is true the page filled to &#x60;limit&#x60; and more changes are already waiting — poll again immediately; otherwise wait your normal interval (e.g. 5–15s). This is NOT pagination — it is a time-keyed change feed. Use the paginated GET /job-requests for the initial bulk load, then this endpoint to stay live. Filters (status_keys, customer_id, …) narrow the feed to the slice you care about.
745
+ # @param [Hash] opts the optional parameters
746
+ # @option opts [String] :status_keys Comma-separated status slugs — only surface changes to jobs in these statuses
747
+ # @option opts [String] :priority Priority filter (p0|p1|p2|p3)
748
+ # @option opts [String] :customer_id Only changes to this customer&#39;s jobs (UUID)
749
+ # @option opts [String] :technician_id Only changes to jobs assigned to this technician (UUID)
750
+ # @option opts [String] :scheduled_from Filter from (YYYY-MM-DD &#x3D; start of that day in the business timezone, or RFC3339); range is [from, to)
751
+ # @option opts [String] :scheduled_to Filter to (YYYY-MM-DD &#x3D; end of that day in the business timezone, or RFC3339), exclusive
752
+ # @option opts [String] :since RFC3339 cursor from the prior response&#39;s next_since. OMIT on the first poll to prime the cursor at server-now.
753
+ # @option opts [Integer] :limit Max changes per poll (default 15, max 1000). If the page fills, has_more&#x3D;true.
754
+ # @return [Array<(ListJobRequestChanges200Response, Integer, Hash)>] ListJobRequestChanges200Response data, response status code and response headers
755
+ def list_job_request_changes_with_http_info(opts = {})
756
+ if @api_client.config.debugging
757
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_job_request_changes ...'
758
+ end
759
+ # resource path
760
+ local_var_path = '/job-requests/changes'
761
+
762
+ # query parameters
763
+ query_params = opts[:query_params] || {}
764
+ query_params[:'status_keys'] = opts[:'status_keys'] if !opts[:'status_keys'].nil?
765
+ query_params[:'priority'] = opts[:'priority'] if !opts[:'priority'].nil?
766
+ query_params[:'customer_id'] = opts[:'customer_id'] if !opts[:'customer_id'].nil?
767
+ query_params[:'technician_id'] = opts[:'technician_id'] if !opts[:'technician_id'].nil?
768
+ query_params[:'scheduled_from'] = opts[:'scheduled_from'] if !opts[:'scheduled_from'].nil?
769
+ query_params[:'scheduled_to'] = opts[:'scheduled_to'] if !opts[:'scheduled_to'].nil?
770
+ query_params[:'since'] = opts[:'since'] if !opts[:'since'].nil?
771
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
772
+
773
+ # header parameters
774
+ header_params = opts[:header_params] || {}
775
+ # HTTP header 'Accept' (if needed)
776
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
777
+
778
+ # form parameters
779
+ form_params = opts[:form_params] || {}
780
+
781
+ # http body (model)
782
+ post_body = opts[:debug_body]
783
+
784
+ # return_type
785
+ return_type = opts[:debug_return_type] || 'ListJobRequestChanges200Response'
786
+
787
+ # auth_names
788
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
789
+
790
+ new_options = opts.merge(
791
+ :operation => :"JobRequestBusinessApi.list_job_request_changes",
792
+ :header_params => header_params,
793
+ :query_params => query_params,
794
+ :form_params => form_params,
795
+ :body => post_body,
796
+ :auth_names => auth_names,
797
+ :return_type => return_type
798
+ )
799
+
800
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
801
+ if @api_client.config.debugging
802
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_job_request_changes\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
803
+ end
804
+ return data, status_code, headers
805
+ end
806
+
807
+ # List job requests
808
+ # Paginated list of the business's bookings (work orders) with dispatch-oriented filters: workflow status, customer, assigned technician, scheduled date range and free-text search over code/description. This is also the SCHEDULE query: combine technician_id + scheduled_from/scheduled_to to read one technician's agenda for a day or week (e.g. \"what is Alex doing tomorrow\"), or just the date range for the whole team's calendar.
809
+ # @param [Hash] opts the optional parameters
810
+ # @option opts [String] :status_keys Comma-separated status slugs
811
+ # @option opts [String] :status active (default) | archived | all
812
+ # @option opts [String] :priority Priority filter (p0|p1|p2|p3)
813
+ # @option opts [String] :customer_id Customer UUID
814
+ # @option opts [String] :technician_id Technician UUID
815
+ # @option opts [String] :service_area_id Service-area UUID (board zone filter)
816
+ # @option opts [String] :scheduled_from Filter from (YYYY-MM-DD &#x3D; start of that day in the business timezone, or RFC3339); range is [from, to)
817
+ # @option opts [String] :scheduled_to Filter to (YYYY-MM-DD &#x3D; end of that day in the business timezone, or RFC3339), exclusive
818
+ # @option opts [String] :q Search short_code or description (case-insensitive, partial match)
819
+ # @option opts [String] :sort Sort key: created_at:desc (default) | created_at:asc | scheduled_at:asc | scheduled_at:desc | priority:asc (P0 first) | priority:desc
820
+ # @option opts [Integer] :page Page number
821
+ # @option opts [Integer] :limit Page size
822
+ # @return [ListJobRequests200Response]
823
+ def list_job_requests(opts = {})
824
+ data, _status_code, _headers = list_job_requests_with_http_info(opts)
825
+ data
826
+ end
827
+
828
+ # List job requests
829
+ # Paginated list of the business&#39;s bookings (work orders) with dispatch-oriented filters: workflow status, customer, assigned technician, scheduled date range and free-text search over code/description. This is also the SCHEDULE query: combine technician_id + scheduled_from/scheduled_to to read one technician&#39;s agenda for a day or week (e.g. \&quot;what is Alex doing tomorrow\&quot;), or just the date range for the whole team&#39;s calendar.
830
+ # @param [Hash] opts the optional parameters
831
+ # @option opts [String] :status_keys Comma-separated status slugs
832
+ # @option opts [String] :status active (default) | archived | all
833
+ # @option opts [String] :priority Priority filter (p0|p1|p2|p3)
834
+ # @option opts [String] :customer_id Customer UUID
835
+ # @option opts [String] :technician_id Technician UUID
836
+ # @option opts [String] :service_area_id Service-area UUID (board zone filter)
837
+ # @option opts [String] :scheduled_from Filter from (YYYY-MM-DD &#x3D; start of that day in the business timezone, or RFC3339); range is [from, to)
838
+ # @option opts [String] :scheduled_to Filter to (YYYY-MM-DD &#x3D; end of that day in the business timezone, or RFC3339), exclusive
839
+ # @option opts [String] :q Search short_code or description (case-insensitive, partial match)
840
+ # @option opts [String] :sort Sort key: created_at:desc (default) | created_at:asc | scheduled_at:asc | scheduled_at:desc | priority:asc (P0 first) | priority:desc
841
+ # @option opts [Integer] :page Page number
842
+ # @option opts [Integer] :limit Page size
843
+ # @return [Array<(ListJobRequests200Response, Integer, Hash)>] ListJobRequests200Response data, response status code and response headers
844
+ def list_job_requests_with_http_info(opts = {})
845
+ if @api_client.config.debugging
846
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_job_requests ...'
847
+ end
848
+ # resource path
849
+ local_var_path = '/job-requests'
850
+
851
+ # query parameters
852
+ query_params = opts[:query_params] || {}
853
+ query_params[:'status_keys'] = opts[:'status_keys'] if !opts[:'status_keys'].nil?
854
+ query_params[:'status'] = opts[:'status'] if !opts[:'status'].nil?
855
+ query_params[:'priority'] = opts[:'priority'] if !opts[:'priority'].nil?
856
+ query_params[:'customer_id'] = opts[:'customer_id'] if !opts[:'customer_id'].nil?
857
+ query_params[:'technician_id'] = opts[:'technician_id'] if !opts[:'technician_id'].nil?
858
+ query_params[:'service_area_id'] = opts[:'service_area_id'] if !opts[:'service_area_id'].nil?
859
+ query_params[:'scheduled_from'] = opts[:'scheduled_from'] if !opts[:'scheduled_from'].nil?
860
+ query_params[:'scheduled_to'] = opts[:'scheduled_to'] if !opts[:'scheduled_to'].nil?
861
+ query_params[:'q'] = opts[:'q'] if !opts[:'q'].nil?
862
+ query_params[:'sort'] = opts[:'sort'] if !opts[:'sort'].nil?
863
+ query_params[:'page'] = opts[:'page'] if !opts[:'page'].nil?
864
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
865
+
866
+ # header parameters
867
+ header_params = opts[:header_params] || {}
868
+ # HTTP header 'Accept' (if needed)
869
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
870
+
871
+ # form parameters
872
+ form_params = opts[:form_params] || {}
873
+
874
+ # http body (model)
875
+ post_body = opts[:debug_body]
876
+
877
+ # return_type
878
+ return_type = opts[:debug_return_type] || 'ListJobRequests200Response'
879
+
880
+ # auth_names
881
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
882
+
883
+ new_options = opts.merge(
884
+ :operation => :"JobRequestBusinessApi.list_job_requests",
885
+ :header_params => header_params,
886
+ :query_params => query_params,
887
+ :form_params => form_params,
888
+ :body => post_body,
889
+ :auth_names => auth_names,
890
+ :return_type => return_type
891
+ )
892
+
893
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
894
+ if @api_client.config.debugging
895
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_job_requests\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
896
+ end
897
+ return data, status_code, headers
898
+ end
899
+
900
+ # Matching time slots for a quoted job
901
+ # Bookable arrival-window slots for a quoted job, computed by the smart-assignment matching engine: each slot lists the technicians actually available to start then (skills, weekly availability, existing schedule, time off and travel all checked), with a per-technician match score. Use it to find and offer appointment times an agent or integration can then confirm (POST /job-requests/{id}/confirm with the slot's business_time.datetime). Same grid the end-customer's slot picker shows; slot width defaults to the business's arrival window — override via ?step_minutes (5–240). The job must be quoted first (the quote sets the visit duration the matcher schedules).
902
+ # @param id [String] Job request ID (UUID or short_code)
903
+ # @param [Hash] opts the optional parameters
904
+ # @option opts [Integer] :step_minutes Slot step in minutes (default: business arrival window, 5–240)
905
+ # @return [ListMatchingSlots200Response]
906
+ def list_matching_slots(id, opts = {})
907
+ data, _status_code, _headers = list_matching_slots_with_http_info(id, opts)
908
+ data
909
+ end
910
+
911
+ # Matching time slots for a quoted job
912
+ # Bookable arrival-window slots for a quoted job, computed by the smart-assignment matching engine: each slot lists the technicians actually available to start then (skills, weekly availability, existing schedule, time off and travel all checked), with a per-technician match score. Use it to find and offer appointment times an agent or integration can then confirm (POST /job-requests/{id}/confirm with the slot&#39;s business_time.datetime). Same grid the end-customer&#39;s slot picker shows; slot width defaults to the business&#39;s arrival window — override via ?step_minutes (5–240). The job must be quoted first (the quote sets the visit duration the matcher schedules).
913
+ # @param id [String] Job request ID (UUID or short_code)
914
+ # @param [Hash] opts the optional parameters
915
+ # @option opts [Integer] :step_minutes Slot step in minutes (default: business arrival window, 5–240)
916
+ # @return [Array<(ListMatchingSlots200Response, Integer, Hash)>] ListMatchingSlots200Response data, response status code and response headers
917
+ def list_matching_slots_with_http_info(id, opts = {})
918
+ if @api_client.config.debugging
919
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_matching_slots ...'
920
+ end
921
+ # verify the required parameter 'id' is set
922
+ if @api_client.config.client_side_validation && id.nil?
923
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobRequestBusinessApi.list_matching_slots"
924
+ end
925
+ # resource path
926
+ local_var_path = '/job-requests/{id}/time-segments'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
927
+
928
+ # query parameters
929
+ query_params = opts[:query_params] || {}
930
+ query_params[:'step_minutes'] = opts[:'step_minutes'] if !opts[:'step_minutes'].nil?
931
+
932
+ # header parameters
933
+ header_params = opts[:header_params] || {}
934
+ # HTTP header 'Accept' (if needed)
935
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
936
+
937
+ # form parameters
938
+ form_params = opts[:form_params] || {}
939
+
940
+ # http body (model)
941
+ post_body = opts[:debug_body]
942
+
943
+ # return_type
944
+ return_type = opts[:debug_return_type] || 'ListMatchingSlots200Response'
945
+
946
+ # auth_names
947
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
948
+
949
+ new_options = opts.merge(
950
+ :operation => :"JobRequestBusinessApi.list_matching_slots",
951
+ :header_params => header_params,
952
+ :query_params => query_params,
953
+ :form_params => form_params,
954
+ :body => post_body,
955
+ :auth_names => auth_names,
956
+ :return_type => return_type
957
+ )
958
+
959
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
960
+ if @api_client.config.debugging
961
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_matching_slots\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
962
+ end
963
+ return data, status_code, headers
964
+ end
965
+
966
+ # Find nearby feasible technicians (job-less location query)
967
+ # Ranks who could serve a hypothetical visit at (lat,lng) starting `at` for `duration_minutes` — the engine applies the REAL hard filters (weekly hours, existing schedule, approved time-off, geographic service areas, optional skill floor) and returns candidates nearest-arrival first. ETA origin is each technician's start location (no live GPS). Use before creating a booking to propose realistic arrivals.
968
+ # @param lat [Float] Latitude of the service location
969
+ # @param lng [Float] Longitude of the service location
970
+ # @param [Hash] opts the optional parameters
971
+ # @option opts [String] :at Visit start (RFC3339, e.g. 2026-07-20T14:00:00Z; default now)
972
+ # @option opts [Integer] :duration_minutes Visit length in minutes (default 60; 15–480)
973
+ # @option opts [String] :skill_ids Comma-separated skill UUIDs to require/match
974
+ # @option opts [Integer] :limit Max candidates (default 10, max 20)
975
+ # @return [ListNearbyTechnicians200Response]
976
+ def list_nearby_technicians(lat, lng, opts = {})
977
+ data, _status_code, _headers = list_nearby_technicians_with_http_info(lat, lng, opts)
978
+ data
979
+ end
980
+
981
+ # Find nearby feasible technicians (job-less location query)
982
+ # Ranks who could serve a hypothetical visit at (lat,lng) starting &#x60;at&#x60; for &#x60;duration_minutes&#x60; — the engine applies the REAL hard filters (weekly hours, existing schedule, approved time-off, geographic service areas, optional skill floor) and returns candidates nearest-arrival first. ETA origin is each technician&#39;s start location (no live GPS). Use before creating a booking to propose realistic arrivals.
983
+ # @param lat [Float] Latitude of the service location
984
+ # @param lng [Float] Longitude of the service location
985
+ # @param [Hash] opts the optional parameters
986
+ # @option opts [String] :at Visit start (RFC3339, e.g. 2026-07-20T14:00:00Z; default now)
987
+ # @option opts [Integer] :duration_minutes Visit length in minutes (default 60; 15–480)
988
+ # @option opts [String] :skill_ids Comma-separated skill UUIDs to require/match
989
+ # @option opts [Integer] :limit Max candidates (default 10, max 20)
990
+ # @return [Array<(ListNearbyTechnicians200Response, Integer, Hash)>] ListNearbyTechnicians200Response data, response status code and response headers
991
+ def list_nearby_technicians_with_http_info(lat, lng, opts = {})
992
+ if @api_client.config.debugging
993
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.list_nearby_technicians ...'
994
+ end
995
+ # verify the required parameter 'lat' is set
996
+ if @api_client.config.client_side_validation && lat.nil?
997
+ fail ArgumentError, "Missing the required parameter 'lat' when calling JobRequestBusinessApi.list_nearby_technicians"
998
+ end
999
+ # verify the required parameter 'lng' is set
1000
+ if @api_client.config.client_side_validation && lng.nil?
1001
+ fail ArgumentError, "Missing the required parameter 'lng' when calling JobRequestBusinessApi.list_nearby_technicians"
1002
+ end
1003
+ # resource path
1004
+ local_var_path = '/technicians/nearby'
1005
+
1006
+ # query parameters
1007
+ query_params = opts[:query_params] || {}
1008
+ query_params[:'lat'] = lat
1009
+ query_params[:'lng'] = lng
1010
+ query_params[:'at'] = opts[:'at'] if !opts[:'at'].nil?
1011
+ query_params[:'duration_minutes'] = opts[:'duration_minutes'] if !opts[:'duration_minutes'].nil?
1012
+ query_params[:'skill_ids'] = opts[:'skill_ids'] if !opts[:'skill_ids'].nil?
1013
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
1014
+
1015
+ # header parameters
1016
+ header_params = opts[:header_params] || {}
1017
+ # HTTP header 'Accept' (if needed)
1018
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
1019
+
1020
+ # form parameters
1021
+ form_params = opts[:form_params] || {}
1022
+
1023
+ # http body (model)
1024
+ post_body = opts[:debug_body]
1025
+
1026
+ # return_type
1027
+ return_type = opts[:debug_return_type] || 'ListNearbyTechnicians200Response'
1028
+
1029
+ # auth_names
1030
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
1031
+
1032
+ new_options = opts.merge(
1033
+ :operation => :"JobRequestBusinessApi.list_nearby_technicians",
1034
+ :header_params => header_params,
1035
+ :query_params => query_params,
1036
+ :form_params => form_params,
1037
+ :body => post_body,
1038
+ :auth_names => auth_names,
1039
+ :return_type => return_type
1040
+ )
1041
+
1042
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
1043
+ if @api_client.config.debugging
1044
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#list_nearby_technicians\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1045
+ end
1046
+ return data, status_code, headers
1047
+ end
1048
+
1049
+ # Preview emergency insert + cascade reschedule
1050
+ # Computes (WITHOUT writing) the cascade of inserting an emergency job onto a technician at a chosen time: where the emergency lands + every job pushed back, grouped per business-local day. `displacement_mode=reassign` instead hands each displaced job to another feasible technician at its ORIGINAL window (same-day promise) — jobs with no alternate capacity fall back to reschedule and stay in `days`. `mode=overtime` keeps everyone same-day (tech works late); `mode=next_day` rolls overflow to the next working day(s). Read-only — safe to call repeatedly; commit is a separate endpoint. Isolated feature (see EMERGENCY_RESCHEDULE_DESIGN.md). 409 NEXT STEPS: EMERGENCY_RESCHEDULE_SLOT_OCCUPIED — the landing window is blocked by a job the cascade may NOT move (another P0, a crew or multi-day job): choose another technician (walk the /candidates ranking) or another time; displacement never touches P0/crew/multi-day anchors. EMERGENCY_RESCHEDULE_NOT_ELIGIBLE / CREW_UNSUPPORTED / MULTIDAY_UNSUPPORTED / NO_WORKING_DAY / IN_PAST — same remedies as /candidates.
1051
+ # @param job_request_emergency_preview_request [JobRequestEmergencyPreviewRequest] emergency insert spec
1052
+ # @param [Hash] opts the optional parameters
1053
+ # @return [CommitEmergencyReschedule200Response]
1054
+ def preview_emergency_reschedule(job_request_emergency_preview_request, opts = {})
1055
+ data, _status_code, _headers = preview_emergency_reschedule_with_http_info(job_request_emergency_preview_request, opts)
1056
+ data
1057
+ end
1058
+
1059
+ # Preview emergency insert + cascade reschedule
1060
+ # Computes (WITHOUT writing) the cascade of inserting an emergency job onto a technician at a chosen time: where the emergency lands + every job pushed back, grouped per business-local day. &#x60;displacement_mode&#x3D;reassign&#x60; instead hands each displaced job to another feasible technician at its ORIGINAL window (same-day promise) — jobs with no alternate capacity fall back to reschedule and stay in &#x60;days&#x60;. &#x60;mode&#x3D;overtime&#x60; keeps everyone same-day (tech works late); &#x60;mode&#x3D;next_day&#x60; rolls overflow to the next working day(s). Read-only — safe to call repeatedly; commit is a separate endpoint. Isolated feature (see EMERGENCY_RESCHEDULE_DESIGN.md). 409 NEXT STEPS: EMERGENCY_RESCHEDULE_SLOT_OCCUPIED — the landing window is blocked by a job the cascade may NOT move (another P0, a crew or multi-day job): choose another technician (walk the /candidates ranking) or another time; displacement never touches P0/crew/multi-day anchors. EMERGENCY_RESCHEDULE_NOT_ELIGIBLE / CREW_UNSUPPORTED / MULTIDAY_UNSUPPORTED / NO_WORKING_DAY / IN_PAST — same remedies as /candidates.
1061
+ # @param job_request_emergency_preview_request [JobRequestEmergencyPreviewRequest] emergency insert spec
1062
+ # @param [Hash] opts the optional parameters
1063
+ # @return [Array<(CommitEmergencyReschedule200Response, Integer, Hash)>] CommitEmergencyReschedule200Response data, response status code and response headers
1064
+ def preview_emergency_reschedule_with_http_info(job_request_emergency_preview_request, opts = {})
1065
+ if @api_client.config.debugging
1066
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.preview_emergency_reschedule ...'
1067
+ end
1068
+ # verify the required parameter 'job_request_emergency_preview_request' is set
1069
+ if @api_client.config.client_side_validation && job_request_emergency_preview_request.nil?
1070
+ fail ArgumentError, "Missing the required parameter 'job_request_emergency_preview_request' when calling JobRequestBusinessApi.preview_emergency_reschedule"
1071
+ end
1072
+ # resource path
1073
+ local_var_path = '/job-requests/emergency/preview'
1074
+
1075
+ # query parameters
1076
+ query_params = opts[:query_params] || {}
1077
+
1078
+ # header parameters
1079
+ header_params = opts[:header_params] || {}
1080
+ # HTTP header 'Accept' (if needed)
1081
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
1082
+ # HTTP header 'Content-Type'
1083
+ content_type = @api_client.select_header_content_type(['application/json'])
1084
+ if !content_type.nil?
1085
+ header_params['Content-Type'] = content_type
1086
+ end
1087
+
1088
+ # form parameters
1089
+ form_params = opts[:form_params] || {}
1090
+
1091
+ # http body (model)
1092
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_request_emergency_preview_request)
1093
+
1094
+ # return_type
1095
+ return_type = opts[:debug_return_type] || 'CommitEmergencyReschedule200Response'
1096
+
1097
+ # auth_names
1098
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
1099
+
1100
+ new_options = opts.merge(
1101
+ :operation => :"JobRequestBusinessApi.preview_emergency_reschedule",
1102
+ :header_params => header_params,
1103
+ :query_params => query_params,
1104
+ :form_params => form_params,
1105
+ :body => post_body,
1106
+ :auth_names => auth_names,
1107
+ :return_type => return_type
1108
+ )
1109
+
1110
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
1111
+ if @api_client.config.debugging
1112
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#preview_emergency_reschedule\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1113
+ end
1114
+ return data, status_code, headers
1115
+ end
1116
+
1117
+ # Preview a schedule-board job move
1118
+ # Computes (WITHOUT writing) the outcome of moving a confirmed job to a new time and/or technician: where it lands, every later job pushed back per `mode`, and the warnings the coordinator would accept (displaced jobs leaving their confirmed windows, overtime). Same technician = pure time move; different technician = manual reassign. Read-only — safe to call repeatedly while dragging; commit is a separate endpoint. See SCHEDULE_BOARD_DESIGN.md. Warning detail: a TECH_NOT_FEASIBLE warning carries `reason` = `cannot_arrive_in_time` (commute from the tech day-start location / shift start; `earliest_feasible_at` (RFC3339 UTC) is the first same-day time they CAN be on site — suggest it as the drop slot) | `missing_required_skills` | `not_available_today` (no working hours, approved time off, or outside the service area) | `not_lead_tier`. For a P0 move this warning is advisory (coordinator may commit anyway); for p1/p2/p3 the same condition is the hard 409 SCHEDULE_MOVE_TECH_INFEASIBLE. 409 NEXT STEPS: SCHEDULE_MOVE_NOT_ELIGIBLE (job unconfirmed/unquoted/archived/completed — not movable) · SCHEDULE_MOVE_IN_PROGRESS (tech already executing — do not move) · SCHEDULE_MOVE_IN_PAST (pick a future time) · SCHEDULE_MOVE_OUTSIDE_WINDOW (landing time outside the customer-confirmed window — hard block; pick a time inside it) · SCHEDULE_MOVE_SLOT_OCCUPIED (landing window blocked by an immovable anchor — another tech/time) · SCHEDULE_MOVE_TECH_INFEASIBLE (non-P0 hard block: target tech not qualified/available — see the TECH_NOT_FEASIBLE warning reasons; change tech or time) · SCHEDULE_MOVE_MULTIDAY_UNSUPPORTED (multi-day jobs not movable v1) · SCHEDULE_MOVE_NO_WORKING_DAY (pick a working day) · SCHEDULE_MOVE_REQUIRES_FREE_SLOT (non-P0 moves may not displace — free capacity only, unless the owner enables allow_non_p0_displacement) · SCHEDULE_MOVE_CREW_UNSTAFFABLE (a crew slot has no feasible replacement at the new time — another time).
1119
+ # @param id [String] Job request ID (UUID or short_code)
1120
+ # @param job_request_move_preview_req [JobRequestMovePreviewReq] move spec
1121
+ # @param [Hash] opts the optional parameters
1122
+ # @return [CommitJobRequestMove200Response]
1123
+ def preview_job_request_move(id, job_request_move_preview_req, opts = {})
1124
+ data, _status_code, _headers = preview_job_request_move_with_http_info(id, job_request_move_preview_req, opts)
1125
+ data
1126
+ end
1127
+
1128
+ # Preview a schedule-board job move
1129
+ # Computes (WITHOUT writing) the outcome of moving a confirmed job to a new time and/or technician: where it lands, every later job pushed back per &#x60;mode&#x60;, and the warnings the coordinator would accept (displaced jobs leaving their confirmed windows, overtime). Same technician &#x3D; pure time move; different technician &#x3D; manual reassign. Read-only — safe to call repeatedly while dragging; commit is a separate endpoint. See SCHEDULE_BOARD_DESIGN.md. Warning detail: a TECH_NOT_FEASIBLE warning carries &#x60;reason&#x60; &#x3D; &#x60;cannot_arrive_in_time&#x60; (commute from the tech day-start location / shift start; &#x60;earliest_feasible_at&#x60; (RFC3339 UTC) is the first same-day time they CAN be on site — suggest it as the drop slot) | &#x60;missing_required_skills&#x60; | &#x60;not_available_today&#x60; (no working hours, approved time off, or outside the service area) | &#x60;not_lead_tier&#x60;. For a P0 move this warning is advisory (coordinator may commit anyway); for p1/p2/p3 the same condition is the hard 409 SCHEDULE_MOVE_TECH_INFEASIBLE. 409 NEXT STEPS: SCHEDULE_MOVE_NOT_ELIGIBLE (job unconfirmed/unquoted/archived/completed — not movable) · SCHEDULE_MOVE_IN_PROGRESS (tech already executing — do not move) · SCHEDULE_MOVE_IN_PAST (pick a future time) · SCHEDULE_MOVE_OUTSIDE_WINDOW (landing time outside the customer-confirmed window — hard block; pick a time inside it) · SCHEDULE_MOVE_SLOT_OCCUPIED (landing window blocked by an immovable anchor — another tech/time) · SCHEDULE_MOVE_TECH_INFEASIBLE (non-P0 hard block: target tech not qualified/available — see the TECH_NOT_FEASIBLE warning reasons; change tech or time) · SCHEDULE_MOVE_MULTIDAY_UNSUPPORTED (multi-day jobs not movable v1) · SCHEDULE_MOVE_NO_WORKING_DAY (pick a working day) · SCHEDULE_MOVE_REQUIRES_FREE_SLOT (non-P0 moves may not displace — free capacity only, unless the owner enables allow_non_p0_displacement) · SCHEDULE_MOVE_CREW_UNSTAFFABLE (a crew slot has no feasible replacement at the new time — another time).
1130
+ # @param id [String] Job request ID (UUID or short_code)
1131
+ # @param job_request_move_preview_req [JobRequestMovePreviewReq] move spec
1132
+ # @param [Hash] opts the optional parameters
1133
+ # @return [Array<(CommitJobRequestMove200Response, Integer, Hash)>] CommitJobRequestMove200Response data, response status code and response headers
1134
+ def preview_job_request_move_with_http_info(id, job_request_move_preview_req, opts = {})
1135
+ if @api_client.config.debugging
1136
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.preview_job_request_move ...'
1137
+ end
1138
+ # verify the required parameter 'id' is set
1139
+ if @api_client.config.client_side_validation && id.nil?
1140
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobRequestBusinessApi.preview_job_request_move"
1141
+ end
1142
+ # verify the required parameter 'job_request_move_preview_req' is set
1143
+ if @api_client.config.client_side_validation && job_request_move_preview_req.nil?
1144
+ fail ArgumentError, "Missing the required parameter 'job_request_move_preview_req' when calling JobRequestBusinessApi.preview_job_request_move"
1145
+ end
1146
+ # resource path
1147
+ local_var_path = '/job-requests/{id}/move/preview'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
1148
+
1149
+ # query parameters
1150
+ query_params = opts[:query_params] || {}
1151
+
1152
+ # header parameters
1153
+ header_params = opts[:header_params] || {}
1154
+ # HTTP header 'Accept' (if needed)
1155
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
1156
+ # HTTP header 'Content-Type'
1157
+ content_type = @api_client.select_header_content_type(['application/json'])
1158
+ if !content_type.nil?
1159
+ header_params['Content-Type'] = content_type
1160
+ end
1161
+
1162
+ # form parameters
1163
+ form_params = opts[:form_params] || {}
1164
+
1165
+ # http body (model)
1166
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_request_move_preview_req)
1167
+
1168
+ # return_type
1169
+ return_type = opts[:debug_return_type] || 'CommitJobRequestMove200Response'
1170
+
1171
+ # auth_names
1172
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
1173
+
1174
+ new_options = opts.merge(
1175
+ :operation => :"JobRequestBusinessApi.preview_job_request_move",
1176
+ :header_params => header_params,
1177
+ :query_params => query_params,
1178
+ :form_params => form_params,
1179
+ :body => post_body,
1180
+ :auth_names => auth_names,
1181
+ :return_type => return_type
1182
+ )
1183
+
1184
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
1185
+ if @api_client.config.debugging
1186
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#preview_job_request_move\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1187
+ end
1188
+ return data, status_code, headers
1189
+ end
1190
+
1191
+ # Fire quote (FIXED action — business)
1192
+ # Sends the quote: sets quoted_at + duration cols, advances pending_action to confirm_booking. Status stays `booking`.
1193
+ # @param id [String] Job request ID
1194
+ # @param job_request_quote_request [JobRequestQuoteRequest] Quote payload
1195
+ # @param [Hash] opts the optional parameters
1196
+ # @return [ResponseEnvelope]
1197
+ def quote_job_request(id, job_request_quote_request, opts = {})
1198
+ data, _status_code, _headers = quote_job_request_with_http_info(id, job_request_quote_request, opts)
1199
+ data
1200
+ end
1201
+
1202
+ # Fire quote (FIXED action — business)
1203
+ # Sends the quote: sets quoted_at + duration cols, advances pending_action to confirm_booking. Status stays &#x60;booking&#x60;.
1204
+ # @param id [String] Job request ID
1205
+ # @param job_request_quote_request [JobRequestQuoteRequest] Quote payload
1206
+ # @param [Hash] opts the optional parameters
1207
+ # @return [Array<(ResponseEnvelope, Integer, Hash)>] ResponseEnvelope data, response status code and response headers
1208
+ def quote_job_request_with_http_info(id, job_request_quote_request, opts = {})
1209
+ if @api_client.config.debugging
1210
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.quote_job_request ...'
1211
+ end
1212
+ # verify the required parameter 'id' is set
1213
+ if @api_client.config.client_side_validation && id.nil?
1214
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobRequestBusinessApi.quote_job_request"
1215
+ end
1216
+ # verify the required parameter 'job_request_quote_request' is set
1217
+ if @api_client.config.client_side_validation && job_request_quote_request.nil?
1218
+ fail ArgumentError, "Missing the required parameter 'job_request_quote_request' when calling JobRequestBusinessApi.quote_job_request"
1219
+ end
1220
+ # resource path
1221
+ local_var_path = '/job-requests/{id}/quote'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
1222
+
1223
+ # query parameters
1224
+ query_params = opts[:query_params] || {}
1225
+
1226
+ # header parameters
1227
+ header_params = opts[:header_params] || {}
1228
+ # HTTP header 'Accept' (if needed)
1229
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
1230
+ # HTTP header 'Content-Type'
1231
+ content_type = @api_client.select_header_content_type(['application/json'])
1232
+ if !content_type.nil?
1233
+ header_params['Content-Type'] = content_type
1234
+ end
1235
+
1236
+ # form parameters
1237
+ form_params = opts[:form_params] || {}
1238
+
1239
+ # http body (model)
1240
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_request_quote_request)
1241
+
1242
+ # return_type
1243
+ return_type = opts[:debug_return_type] || 'ResponseEnvelope'
1244
+
1245
+ # auth_names
1246
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
1247
+
1248
+ new_options = opts.merge(
1249
+ :operation => :"JobRequestBusinessApi.quote_job_request",
1250
+ :header_params => header_params,
1251
+ :query_params => query_params,
1252
+ :form_params => form_params,
1253
+ :body => post_body,
1254
+ :auth_names => auth_names,
1255
+ :return_type => return_type
1256
+ )
1257
+
1258
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
1259
+ if @api_client.config.debugging
1260
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#quote_job_request\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1261
+ end
1262
+ return data, status_code, headers
1263
+ end
1264
+
1265
+ # Set job priority (scheduling staff)
1266
+ # Sets the P0–P3 priority on a non-archived, non-completed job (Owner / Administrator / Booking Coordinator). Allowed values: \"p0\" (emergency, interrupt-driven) | \"p1\" (top — displaced only by p0; may carry an sla_deadline arming auto-escalation) | \"p2\" (standard) | \"p3\" (deferrable, first displacement victim). sla_deadline is only valid with p1 and must be in the future (business-local naive datetime); moving away from p1 disarms the SLA clock. Accepts UUID or short_code in :id.
1267
+ # @param id [String] Job request ID or short_code
1268
+ # @param job_request_update_priority_request [JobRequestUpdatePriorityRequest] Priority payload
1269
+ # @param [Hash] opts the optional parameters
1270
+ # @return [ResponseEnvelope]
1271
+ def update_job_priority(id, job_request_update_priority_request, opts = {})
1272
+ data, _status_code, _headers = update_job_priority_with_http_info(id, job_request_update_priority_request, opts)
1273
+ data
1274
+ end
1275
+
1276
+ # Set job priority (scheduling staff)
1277
+ # Sets the P0–P3 priority on a non-archived, non-completed job (Owner / Administrator / Booking Coordinator). Allowed values: \&quot;p0\&quot; (emergency, interrupt-driven) | \&quot;p1\&quot; (top — displaced only by p0; may carry an sla_deadline arming auto-escalation) | \&quot;p2\&quot; (standard) | \&quot;p3\&quot; (deferrable, first displacement victim). sla_deadline is only valid with p1 and must be in the future (business-local naive datetime); moving away from p1 disarms the SLA clock. Accepts UUID or short_code in :id.
1278
+ # @param id [String] Job request ID or short_code
1279
+ # @param job_request_update_priority_request [JobRequestUpdatePriorityRequest] Priority payload
1280
+ # @param [Hash] opts the optional parameters
1281
+ # @return [Array<(ResponseEnvelope, Integer, Hash)>] ResponseEnvelope data, response status code and response headers
1282
+ def update_job_priority_with_http_info(id, job_request_update_priority_request, opts = {})
1283
+ if @api_client.config.debugging
1284
+ @api_client.config.logger.debug 'Calling API: JobRequestBusinessApi.update_job_priority ...'
1285
+ end
1286
+ # verify the required parameter 'id' is set
1287
+ if @api_client.config.client_side_validation && id.nil?
1288
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobRequestBusinessApi.update_job_priority"
1289
+ end
1290
+ # verify the required parameter 'job_request_update_priority_request' is set
1291
+ if @api_client.config.client_side_validation && job_request_update_priority_request.nil?
1292
+ fail ArgumentError, "Missing the required parameter 'job_request_update_priority_request' when calling JobRequestBusinessApi.update_job_priority"
1293
+ end
1294
+ # resource path
1295
+ local_var_path = '/job-requests/{id}/priority'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
1296
+
1297
+ # query parameters
1298
+ query_params = opts[:query_params] || {}
1299
+
1300
+ # header parameters
1301
+ header_params = opts[:header_params] || {}
1302
+ # HTTP header 'Accept' (if needed)
1303
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
1304
+ # HTTP header 'Content-Type'
1305
+ content_type = @api_client.select_header_content_type(['application/json'])
1306
+ if !content_type.nil?
1307
+ header_params['Content-Type'] = content_type
1308
+ end
1309
+
1310
+ # form parameters
1311
+ form_params = opts[:form_params] || {}
1312
+
1313
+ # http body (model)
1314
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_request_update_priority_request)
1315
+
1316
+ # return_type
1317
+ return_type = opts[:debug_return_type] || 'ResponseEnvelope'
1318
+
1319
+ # auth_names
1320
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
1321
+
1322
+ new_options = opts.merge(
1323
+ :operation => :"JobRequestBusinessApi.update_job_priority",
1324
+ :header_params => header_params,
1325
+ :query_params => query_params,
1326
+ :form_params => form_params,
1327
+ :body => post_body,
1328
+ :auth_names => auth_names,
1329
+ :return_type => return_type
1330
+ )
1331
+
1332
+ data, status_code, headers = @api_client.call_api(:PATCH, local_var_path, new_options)
1333
+ if @api_client.config.debugging
1334
+ @api_client.config.logger.debug "API called: JobRequestBusinessApi#update_job_priority\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
445
1335
  end
446
1336
  return data, status_code, headers
447
1337
  end