crisphive 0.2.0 → 0.3.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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +98 -10
  3. data/lib/crisphive/api/business_skill_api.rb +348 -0
  4. data/lib/crisphive/api/customer_api.rb +7 -4
  5. data/lib/crisphive/api/job_request_business_api.rb +243 -27
  6. data/lib/crisphive/api/job_types_api.rb +208 -0
  7. data/lib/crisphive/api/permission_api.rb +79 -0
  8. data/lib/crisphive/api/service_area_api.rb +208 -0
  9. data/lib/crisphive/api/technician_api.rb +8 -8
  10. data/lib/crisphive/api/technician_time_off_api.rb +240 -0
  11. data/lib/crisphive/api/vehicle_api.rb +208 -0
  12. data/lib/crisphive/api/webhooks_api.rb +213 -0
  13. data/lib/crisphive/models/api_internal_techtimeoff_actions.rb +232 -0
  14. data/lib/crisphive/models/api_internal_techtimeoff_status.rb +42 -0
  15. data/lib/crisphive/models/assignable_group.rb +289 -0
  16. data/lib/crisphive/models/book_and_confirm_job_request200_response.rb +253 -0
  17. data/lib/crisphive/models/commit_absence_resolve200_response.rb +253 -0
  18. data/lib/crisphive/models/create_job_type200_response.rb +253 -0
  19. data/lib/crisphive/models/create_service_area200_response.rb +253 -0
  20. data/lib/crisphive/models/create_skill200_response.rb +253 -0
  21. data/lib/crisphive/models/create_skill_category200_response.rb +253 -0
  22. data/lib/crisphive/models/create_technician_time_off200_response.rb +253 -0
  23. data/lib/crisphive/models/create_vehicle200_response.rb +253 -0
  24. data/lib/crisphive/models/create_webhook_endpoint200_response.rb +253 -0
  25. data/lib/crisphive/models/customer.rb +11 -1
  26. data/lib/crisphive/models/customer_contact.rb +25 -5
  27. data/lib/crisphive/models/customer_create_request.rb +12 -2
  28. data/lib/crisphive/models/customer_list_item.rb +11 -1
  29. data/lib/crisphive/models/customer_quick_books_link.rb +235 -0
  30. data/lib/crisphive/models/customer_service_area_ref.rb +225 -0
  31. data/lib/crisphive/models/customer_technician_ref.rb +225 -0
  32. data/lib/crisphive/models/customer_update_request.rb +23 -20
  33. data/lib/crisphive/models/internal_jobrequest_http_address_req.rb +475 -0
  34. data/lib/crisphive/models/job_request.rb +11 -1
  35. data/lib/crisphive/models/job_request_absence_alternative.rb +356 -0
  36. data/lib/crisphive/models/job_request_absence_alternative_cost.rb +275 -0
  37. data/lib/crisphive/models/job_request_absence_assignment.rb +319 -0
  38. data/lib/crisphive/models/job_request_absence_commit_request.rb +296 -0
  39. data/lib/crisphive/models/job_request_absence_notification.rb +283 -0
  40. data/lib/crisphive/models/job_request_absence_plan.rb +322 -0
  41. data/lib/crisphive/models/job_request_absence_preview_request.rb +280 -0
  42. data/lib/crisphive/models/job_request_absence_resolved_job.rb +499 -0
  43. data/lib/crisphive/models/job_request_absence_solver.rb +289 -0
  44. data/lib/crisphive/models/job_request_absence_time_off.rb +281 -0
  45. data/lib/crisphive/models/job_request_absence_unresolved_job.rb +364 -0
  46. data/lib/crisphive/models/job_request_action_summary.rb +1 -1
  47. data/lib/crisphive/models/job_request_attention_summary.rb +2 -2
  48. data/lib/crisphive/models/job_request_blocker.rb +363 -0
  49. data/lib/crisphive/models/job_request_blocker_ref.rb +223 -0
  50. data/lib/crisphive/models/job_request_book_and_confirm_customer.rb +302 -0
  51. data/lib/crisphive/models/job_request_book_and_confirm_emergency.rb +315 -0
  52. data/lib/crisphive/models/job_request_book_and_confirm_emergency_result.rb +279 -0
  53. data/lib/crisphive/models/job_request_book_and_confirm_refusal.rb +280 -0
  54. data/lib/crisphive/models/job_request_book_and_confirm_request.rb +466 -0
  55. data/lib/crisphive/models/job_request_book_and_confirm_response.rb +295 -0
  56. data/lib/crisphive/models/job_request_confirm_request.rb +14 -4
  57. data/lib/crisphive/models/job_request_create_request.rb +1 -1
  58. data/lib/crisphive/models/job_request_emergency_candidate.rb +26 -4
  59. data/lib/crisphive/models/job_request_emergency_candidates.rb +1 -1
  60. data/lib/crisphive/models/job_request_emergency_candidates_request.rb +24 -11
  61. data/lib/crisphive/models/job_request_emergency_commit_request.rb +11 -1
  62. data/lib/crisphive/models/job_request_emergency_inline_job.rb +353 -0
  63. data/lib/crisphive/models/job_request_emergency_plan.rb +25 -3
  64. data/lib/crisphive/models/job_request_emergency_preview_request.rb +24 -11
  65. data/lib/crisphive/models/job_request_move_commit_req.rb +11 -1
  66. data/lib/crisphive/models/job_request_move_plan.rb +11 -1
  67. data/lib/crisphive/models/job_request_move_preview_req.rb +11 -1
  68. data/lib/crisphive/models/job_request_move_warning.rb +32 -8
  69. data/lib/crisphive/models/job_request_priority_update_result.rb +217 -0
  70. data/lib/crisphive/models/job_request_quote_request.rb +14 -11
  71. data/lib/crisphive/models/job_request_reassign_fallback.rb +287 -0
  72. data/lib/crisphive/models/job_request_skill_summary.rb +1 -1
  73. data/lib/crisphive/models/job_request_status_summary.rb +1 -1
  74. data/lib/crisphive/models/job_request_time_segments.rb +2 -2
  75. data/lib/crisphive/models/job_type.rb +42 -2
  76. data/lib/crisphive/models/job_type_create_request.rb +399 -0
  77. data/lib/crisphive/models/job_type_create_response.rb +215 -0
  78. data/lib/crisphive/models/job_type_update_request.rb +400 -0
  79. data/lib/crisphive/models/list_assignable_groups200_response.rb +255 -0
  80. data/lib/crisphive/models/list_technician_time_off200_response.rb +253 -0
  81. data/lib/crisphive/models/list_webhook_event_types200_response.rb +255 -0
  82. data/lib/crisphive/models/quote_job_request200_response.rb +253 -0
  83. data/lib/crisphive/models/service_area.rb +21 -1
  84. data/lib/crisphive/models/service_area_create_request.rb +455 -0
  85. data/lib/crisphive/models/service_area_create_response.rb +215 -0
  86. data/lib/crisphive/models/service_area_update_request.rb +451 -0
  87. data/lib/crisphive/models/skill_category_create_request.rb +270 -0
  88. data/lib/crisphive/models/skill_category_create_response.rb +215 -0
  89. data/lib/crisphive/models/skill_create_request.rb +270 -0
  90. data/lib/crisphive/models/skill_create_response.rb +215 -0
  91. data/lib/crisphive/models/skill_update_request.rb +281 -0
  92. data/lib/crisphive/models/technician.rb +64 -2
  93. data/lib/crisphive/models/technician_create_request.rb +14 -11
  94. data/lib/crisphive/models/technician_time_off.rb +334 -0
  95. data/lib/crisphive/models/technician_time_off_create_request.rb +285 -0
  96. data/lib/crisphive/models/technician_time_off_create_response.rb +215 -0
  97. data/lib/crisphive/models/technician_time_off_list.rb +246 -0
  98. data/lib/crisphive/models/technician_update_request.rb +2 -2
  99. data/lib/crisphive/models/vehicle_create_request.rb +420 -0
  100. data/lib/crisphive/models/vehicle_create_response.rb +215 -0
  101. data/lib/crisphive/models/vehicle_update_request.rb +440 -0
  102. data/lib/crisphive/models/webhook_create_request.rb +320 -0
  103. data/lib/crisphive/models/webhook_create_response.rb +391 -0
  104. data/lib/crisphive/models/webhook_verification.rb +235 -0
  105. data/lib/crisphive/version.rb +1 -1
  106. data/lib/crisphive.rb +66 -0
  107. metadata +68 -2
@@ -19,6 +19,140 @@ module Crisphive
19
19
  def initialize(api_client = ApiClient.default)
20
20
  @api_client = api_client
21
21
  end
22
+ # Add a job type to the catalog
23
+ # Creates a kind of work customers can book, such as \"Annual boiler service\" or \"Drain unblocking\". Job types classify bookings: createJobRequest takes an optional `job_type_id` from this catalog and the job keeps the type's name as it was at booking time. `name` is the only required field and must be unique in the business (JOB_TYPE_DUPLICATE). `status` defaults to active. An inactive type stays in the catalog but cannot be chosen for new job requests; use that rather than deleting a type you may revive. Send an Idempotency-Key header (the `idempotency_key` argument over MCP) so a retried call replays the original response instead of creating a duplicate type. Optional default_duration_minutes (+ default_mobilization_minutes / default_demobilization_minutes) set how long this kind of work usually takes: quoteJobRequest uses them when it is sent no job_duration_minutes, so an automation or voice agent can schedule the job without knowing the length. A buffer needs a duration (JOB_TYPE_INVALID_DEFAULT_DURATION). This defines the catalog, not a booking. To book actual work use createJobRequest and reference the job type there.
24
+ # @param job_type_create_request [JobTypeCreateRequest] Job type
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 [CreateJobType200Response]
28
+ def create_job_type(job_type_create_request, opts = {})
29
+ data, _status_code, _headers = create_job_type_with_http_info(job_type_create_request, opts)
30
+ data
31
+ end
32
+
33
+ # Add a job type to the catalog
34
+ # Creates a kind of work customers can book, such as \"Annual boiler service\" or \"Drain unblocking\". Job types classify bookings: createJobRequest takes an optional `job_type_id` from this catalog and the job keeps the type's name as it was at booking time. `name` is the only required field and must be unique in the business (JOB_TYPE_DUPLICATE). `status` defaults to active. An inactive type stays in the catalog but cannot be chosen for new job requests; use that rather than deleting a type you may revive. Send an Idempotency-Key header (the `idempotency_key` argument over MCP) so a retried call replays the original response instead of creating a duplicate type. Optional default_duration_minutes (+ default_mobilization_minutes / default_demobilization_minutes) set how long this kind of work usually takes: quoteJobRequest uses them when it is sent no job_duration_minutes, so an automation or voice agent can schedule the job without knowing the length. A buffer needs a duration (JOB_TYPE_INVALID_DEFAULT_DURATION). This defines the catalog, not a booking. To book actual work use createJobRequest and reference the job type there.
35
+ # @param job_type_create_request [JobTypeCreateRequest] Job type
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<(CreateJobType200Response, Integer, Hash)>] CreateJobType200Response data, response status code and response headers
39
+ def create_job_type_with_http_info(job_type_create_request, opts = {})
40
+ if @api_client.config.debugging
41
+ @api_client.config.logger.debug 'Calling API: JobTypesApi.create_job_type ...'
42
+ end
43
+ # verify the required parameter 'job_type_create_request' is set
44
+ if @api_client.config.client_side_validation && job_type_create_request.nil?
45
+ fail ArgumentError, "Missing the required parameter 'job_type_create_request' when calling JobTypesApi.create_job_type"
46
+ end
47
+ # resource path
48
+ local_var_path = '/job-types'
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_type_create_request)
69
+
70
+ # return_type
71
+ return_type = opts[:debug_return_type] || 'CreateJobType200Response'
72
+
73
+ # auth_names
74
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
75
+
76
+ new_options = opts.merge(
77
+ :operation => :"JobTypesApi.create_job_type",
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: JobTypesApi#create_job_type\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
89
+ end
90
+ return data, status_code, headers
91
+ end
92
+
93
+ # Remove a job type from the catalog
94
+ # Soft-deletes the entry: it disappears from the catalog and can no longer be selected for new bookings. Jobs already booked against it are unaffected and keep showing the name they were booked with. Prefer updateJobType with `status=inactive` in almost every case: it has the same effect on the booking form and is trivially reversible. Delete is for a type created in error or one that will never return. Rows the platform ships with (`is_system=true`) cannot be deleted and are refused with JOB_TYPE_SYSTEM_READ_ONLY.
95
+ # @param id [String] Job Type ID
96
+ # @param [Hash] opts the optional parameters
97
+ # @return [ResponseEnvelope]
98
+ def delete_job_type(id, opts = {})
99
+ data, _status_code, _headers = delete_job_type_with_http_info(id, opts)
100
+ data
101
+ end
102
+
103
+ # Remove a job type from the catalog
104
+ # Soft-deletes the entry: it disappears from the catalog and can no longer be selected for new bookings. Jobs already booked against it are unaffected and keep showing the name they were booked with. Prefer updateJobType with &#x60;status&#x3D;inactive&#x60; in almost every case: it has the same effect on the booking form and is trivially reversible. Delete is for a type created in error or one that will never return. Rows the platform ships with (&#x60;is_system&#x3D;true&#x60;) cannot be deleted and are refused with JOB_TYPE_SYSTEM_READ_ONLY.
105
+ # @param id [String] Job Type ID
106
+ # @param [Hash] opts the optional parameters
107
+ # @return [Array<(ResponseEnvelope, Integer, Hash)>] ResponseEnvelope data, response status code and response headers
108
+ def delete_job_type_with_http_info(id, opts = {})
109
+ if @api_client.config.debugging
110
+ @api_client.config.logger.debug 'Calling API: JobTypesApi.delete_job_type ...'
111
+ end
112
+ # verify the required parameter 'id' is set
113
+ if @api_client.config.client_side_validation && id.nil?
114
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobTypesApi.delete_job_type"
115
+ end
116
+ # resource path
117
+ local_var_path = '/job-types/{id}'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
118
+
119
+ # query parameters
120
+ query_params = opts[:query_params] || {}
121
+
122
+ # header parameters
123
+ header_params = opts[:header_params] || {}
124
+ # HTTP header 'Accept' (if needed)
125
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
126
+
127
+ # form parameters
128
+ form_params = opts[:form_params] || {}
129
+
130
+ # http body (model)
131
+ post_body = opts[:debug_body]
132
+
133
+ # return_type
134
+ return_type = opts[:debug_return_type] || 'ResponseEnvelope'
135
+
136
+ # auth_names
137
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
138
+
139
+ new_options = opts.merge(
140
+ :operation => :"JobTypesApi.delete_job_type",
141
+ :header_params => header_params,
142
+ :query_params => query_params,
143
+ :form_params => form_params,
144
+ :body => post_body,
145
+ :auth_names => auth_names,
146
+ :return_type => return_type
147
+ )
148
+
149
+ data, status_code, headers = @api_client.call_api(:DELETE, local_var_path, new_options)
150
+ if @api_client.config.debugging
151
+ @api_client.config.logger.debug "API called: JobTypesApi#delete_job_type\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
152
+ end
153
+ return data, status_code, headers
154
+ end
155
+
22
156
  # Get a job type
23
157
  # Returns one entry of the business's service catalog (job/work-order type) with its localized display name — e.g. an HVAC tune-up, drain cleaning or electrical inspection offering.
24
158
  # @param id [String] Job Type ID
@@ -141,5 +275,79 @@ module Crisphive
141
275
  end
142
276
  return data, status_code, headers
143
277
  end
278
+
279
+ # Rename a job type or change its availability
280
+ # Edits a catalog entry in place. A rename applies to NEW bookings only: every job stores the job-type name it was booked with, so existing and completed jobs keep their original label. Partial update: omit a field to keep it. `name` rejects \"\" because a type must stay identifiable, and must stay unique (JOB_TYPE_DUPLICATE). Setting `status` to inactive is the reversible way to take a type off the booking form; inactive types are refused for new job requests. Default quote bundle: default_duration_minutes / default_mobilization_minutes / default_demobilization_minutes follow the same partial rule; omit to keep, 0 to clear, a value to set. Clearing the duration while a buffer stays is refused (JOB_TYPE_INVALID_DEFAULT_DURATION). Rows the platform ships with (`is_system=true`, e.g. the default \"General\" type) keep their name and status read-only (JOB_TYPE_SYSTEM_READ_ONLY); their default quote bundle IS editable. Create your own type if you need different wording.
281
+ # @param id [String] Job Type ID
282
+ # @param job_type_update_request [JobTypeUpdateRequest] Job type
283
+ # @param [Hash] opts the optional parameters
284
+ # @return [ResponseEnvelope]
285
+ def update_job_type(id, job_type_update_request, opts = {})
286
+ data, _status_code, _headers = update_job_type_with_http_info(id, job_type_update_request, opts)
287
+ data
288
+ end
289
+
290
+ # Rename a job type or change its availability
291
+ # Edits a catalog entry in place. A rename applies to NEW bookings only: every job stores the job-type name it was booked with, so existing and completed jobs keep their original label. Partial update: omit a field to keep it. &#x60;name&#x60; rejects \&quot;\&quot; because a type must stay identifiable, and must stay unique (JOB_TYPE_DUPLICATE). Setting &#x60;status&#x60; to inactive is the reversible way to take a type off the booking form; inactive types are refused for new job requests. Default quote bundle: default_duration_minutes / default_mobilization_minutes / default_demobilization_minutes follow the same partial rule; omit to keep, 0 to clear, a value to set. Clearing the duration while a buffer stays is refused (JOB_TYPE_INVALID_DEFAULT_DURATION). Rows the platform ships with (&#x60;is_system&#x3D;true&#x60;, e.g. the default \&quot;General\&quot; type) keep their name and status read-only (JOB_TYPE_SYSTEM_READ_ONLY); their default quote bundle IS editable. Create your own type if you need different wording.
292
+ # @param id [String] Job Type ID
293
+ # @param job_type_update_request [JobTypeUpdateRequest] Job type
294
+ # @param [Hash] opts the optional parameters
295
+ # @return [Array<(ResponseEnvelope, Integer, Hash)>] ResponseEnvelope data, response status code and response headers
296
+ def update_job_type_with_http_info(id, job_type_update_request, opts = {})
297
+ if @api_client.config.debugging
298
+ @api_client.config.logger.debug 'Calling API: JobTypesApi.update_job_type ...'
299
+ end
300
+ # verify the required parameter 'id' is set
301
+ if @api_client.config.client_side_validation && id.nil?
302
+ fail ArgumentError, "Missing the required parameter 'id' when calling JobTypesApi.update_job_type"
303
+ end
304
+ # verify the required parameter 'job_type_update_request' is set
305
+ if @api_client.config.client_side_validation && job_type_update_request.nil?
306
+ fail ArgumentError, "Missing the required parameter 'job_type_update_request' when calling JobTypesApi.update_job_type"
307
+ end
308
+ # resource path
309
+ local_var_path = '/job-types/{id}'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
310
+
311
+ # query parameters
312
+ query_params = opts[:query_params] || {}
313
+
314
+ # header parameters
315
+ header_params = opts[:header_params] || {}
316
+ # HTTP header 'Accept' (if needed)
317
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
318
+ # HTTP header 'Content-Type'
319
+ content_type = @api_client.select_header_content_type(['application/json'])
320
+ if !content_type.nil?
321
+ header_params['Content-Type'] = content_type
322
+ end
323
+
324
+ # form parameters
325
+ form_params = opts[:form_params] || {}
326
+
327
+ # http body (model)
328
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(job_type_update_request)
329
+
330
+ # return_type
331
+ return_type = opts[:debug_return_type] || 'ResponseEnvelope'
332
+
333
+ # auth_names
334
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
335
+
336
+ new_options = opts.merge(
337
+ :operation => :"JobTypesApi.update_job_type",
338
+ :header_params => header_params,
339
+ :query_params => query_params,
340
+ :form_params => form_params,
341
+ :body => post_body,
342
+ :auth_names => auth_names,
343
+ :return_type => return_type
344
+ )
345
+
346
+ data, status_code, headers = @api_client.call_api(:PUT, local_var_path, new_options)
347
+ if @api_client.config.debugging
348
+ @api_client.config.logger.debug "API called: JobTypesApi#update_job_type\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
349
+ end
350
+ return data, status_code, headers
351
+ end
144
352
  end
145
353
  end
@@ -0,0 +1,79 @@
1
+ =begin
2
+ #Crisphive Developer API
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> }`.
5
+
6
+ The version of the OpenAPI document: 1.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.11.0
10
+
11
+ =end
12
+
13
+ require 'cgi'
14
+
15
+ module Crisphive
16
+ class PermissionApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # List groups the caller may assign to a member
23
+ # Returns the role groups the CURRENT caller is allowed to hand out when creating or re-roling a team member, filtered server-side by the role-assignment ceiling (system groups: at or below the caller's own rank, inclusive; custom groups: only those whose permissions are a subset of the caller's). Build the member-create role dropdown from THIS list — the same rule is enforced on POST/PUT /business/technicians, so anything shown here is guaranteed to be accepted.
24
+ # @param [Hash] opts the optional parameters
25
+ # @return [ListAssignableGroups200Response]
26
+ def list_assignable_groups(opts = {})
27
+ data, _status_code, _headers = list_assignable_groups_with_http_info(opts)
28
+ data
29
+ end
30
+
31
+ # List groups the caller may assign to a member
32
+ # Returns the role groups the CURRENT caller is allowed to hand out when creating or re-roling a team member, filtered server-side by the role-assignment ceiling (system groups: at or below the caller&#39;s own rank, inclusive; custom groups: only those whose permissions are a subset of the caller&#39;s). Build the member-create role dropdown from THIS list — the same rule is enforced on POST/PUT /business/technicians, so anything shown here is guaranteed to be accepted.
33
+ # @param [Hash] opts the optional parameters
34
+ # @return [Array<(ListAssignableGroups200Response, Integer, Hash)>] ListAssignableGroups200Response data, response status code and response headers
35
+ def list_assignable_groups_with_http_info(opts = {})
36
+ if @api_client.config.debugging
37
+ @api_client.config.logger.debug 'Calling API: PermissionApi.list_assignable_groups ...'
38
+ end
39
+ # resource path
40
+ local_var_path = '/permission/groups/assignable'
41
+
42
+ # query parameters
43
+ query_params = opts[:query_params] || {}
44
+
45
+ # header parameters
46
+ header_params = opts[:header_params] || {}
47
+ # HTTP header 'Accept' (if needed)
48
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
49
+
50
+ # form parameters
51
+ form_params = opts[:form_params] || {}
52
+
53
+ # http body (model)
54
+ post_body = opts[:debug_body]
55
+
56
+ # return_type
57
+ return_type = opts[:debug_return_type] || 'ListAssignableGroups200Response'
58
+
59
+ # auth_names
60
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
61
+
62
+ new_options = opts.merge(
63
+ :operation => :"PermissionApi.list_assignable_groups",
64
+ :header_params => header_params,
65
+ :query_params => query_params,
66
+ :form_params => form_params,
67
+ :body => post_body,
68
+ :auth_names => auth_names,
69
+ :return_type => return_type
70
+ )
71
+
72
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
73
+ if @api_client.config.debugging
74
+ @api_client.config.logger.debug "API called: PermissionApi#list_assignable_groups\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
75
+ end
76
+ return data, status_code, headers
77
+ end
78
+ end
79
+ end
@@ -19,6 +19,140 @@ module Crisphive
19
19
  def initialize(api_client = ApiClient.default)
20
20
  @api_client = api_client
21
21
  end
22
+ # Define a territory the business serves
23
+ # Creates a service area: a named region used as a HARD filter when deciding who can take a job. A technician assigned to no area covering the job's address is never offered by listNearbyTechnicians, listMatchingSlots or listCrewCandidates, and never auto-assigned at confirm, whatever their skills or availability say. `name` is the only required field and must be unique (SERVICE_AREA_DUPLICATE_NAME). Coverage is matched two ways: with a `boundary` (GeoJSON polygon), a geocoded address must fall inside the polygon; without one, the area matches addresses by equality on its postal_code, city or district. A polygon is the precise option; the administrative fields are the fallback, and also what matches jobs whose address could not be geocoded. An invalid polygon is refused with SERVICE_AREA_INVALID_BOUNDARY. Creating the area does not staff it. Assign technicians with replaceTechnicianServiceAreas, or pass `service_area_ids` to createTechnician. Send an Idempotency-Key header (the `idempotency_key` argument over MCP) so a retry does not create a duplicate area.
24
+ # @param service_area_create_request [ServiceAreaCreateRequest] Service area details
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 [CreateServiceArea200Response]
28
+ def create_service_area(service_area_create_request, opts = {})
29
+ data, _status_code, _headers = create_service_area_with_http_info(service_area_create_request, opts)
30
+ data
31
+ end
32
+
33
+ # Define a territory the business serves
34
+ # Creates a service area: a named region used as a HARD filter when deciding who can take a job. A technician assigned to no area covering the job&#39;s address is never offered by listNearbyTechnicians, listMatchingSlots or listCrewCandidates, and never auto-assigned at confirm, whatever their skills or availability say. &#x60;name&#x60; is the only required field and must be unique (SERVICE_AREA_DUPLICATE_NAME). Coverage is matched two ways: with a &#x60;boundary&#x60; (GeoJSON polygon), a geocoded address must fall inside the polygon; without one, the area matches addresses by equality on its postal_code, city or district. A polygon is the precise option; the administrative fields are the fallback, and also what matches jobs whose address could not be geocoded. An invalid polygon is refused with SERVICE_AREA_INVALID_BOUNDARY. Creating the area does not staff it. Assign technicians with replaceTechnicianServiceAreas, or pass &#x60;service_area_ids&#x60; to createTechnician. Send an Idempotency-Key header (the &#x60;idempotency_key&#x60; argument over MCP) so a retry does not create a duplicate area.
35
+ # @param service_area_create_request [ServiceAreaCreateRequest] Service area details
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<(CreateServiceArea200Response, Integer, Hash)>] CreateServiceArea200Response data, response status code and response headers
39
+ def create_service_area_with_http_info(service_area_create_request, opts = {})
40
+ if @api_client.config.debugging
41
+ @api_client.config.logger.debug 'Calling API: ServiceAreaApi.create_service_area ...'
42
+ end
43
+ # verify the required parameter 'service_area_create_request' is set
44
+ if @api_client.config.client_side_validation && service_area_create_request.nil?
45
+ fail ArgumentError, "Missing the required parameter 'service_area_create_request' when calling ServiceAreaApi.create_service_area"
46
+ end
47
+ # resource path
48
+ local_var_path = '/service-areas'
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(service_area_create_request)
69
+
70
+ # return_type
71
+ return_type = opts[:debug_return_type] || 'CreateServiceArea200Response'
72
+
73
+ # auth_names
74
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
75
+
76
+ new_options = opts.merge(
77
+ :operation => :"ServiceAreaApi.create_service_area",
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: ServiceAreaApi#create_service_area\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
89
+ end
90
+ return data, status_code, headers
91
+ end
92
+
93
+ # Stop serving a territory
94
+ # Soft-deletes the service area: it disappears from listServiceAreas and stops counting for coverage immediately. Technician assignments to it are left in place but no longer grant coverage. The consequence is easy to underestimate: technicians whose only coverage was this area become unmatchable for addresses inside it. Jobs already assigned keep their technician, but any re-plan (reassign, board move, confirm of a pending job, the slot picker) can find no feasible crew there. Before deleting, re-check listCrewCandidates on upcoming jobs in that territory. If you are reshaping coverage rather than withdrawing from it, edit the polygon or postal/city fields with updateServiceArea instead; that keeps the area and its technician assignments working.
95
+ # @param id [String] Service Area ID
96
+ # @param [Hash] opts the optional parameters
97
+ # @return [ResponseEnvelope]
98
+ def delete_service_area(id, opts = {})
99
+ data, _status_code, _headers = delete_service_area_with_http_info(id, opts)
100
+ data
101
+ end
102
+
103
+ # Stop serving a territory
104
+ # Soft-deletes the service area: it disappears from listServiceAreas and stops counting for coverage immediately. Technician assignments to it are left in place but no longer grant coverage. The consequence is easy to underestimate: technicians whose only coverage was this area become unmatchable for addresses inside it. Jobs already assigned keep their technician, but any re-plan (reassign, board move, confirm of a pending job, the slot picker) can find no feasible crew there. Before deleting, re-check listCrewCandidates on upcoming jobs in that territory. If you are reshaping coverage rather than withdrawing from it, edit the polygon or postal/city fields with updateServiceArea instead; that keeps the area and its technician assignments working.
105
+ # @param id [String] Service Area ID
106
+ # @param [Hash] opts the optional parameters
107
+ # @return [Array<(ResponseEnvelope, Integer, Hash)>] ResponseEnvelope data, response status code and response headers
108
+ def delete_service_area_with_http_info(id, opts = {})
109
+ if @api_client.config.debugging
110
+ @api_client.config.logger.debug 'Calling API: ServiceAreaApi.delete_service_area ...'
111
+ end
112
+ # verify the required parameter 'id' is set
113
+ if @api_client.config.client_side_validation && id.nil?
114
+ fail ArgumentError, "Missing the required parameter 'id' when calling ServiceAreaApi.delete_service_area"
115
+ end
116
+ # resource path
117
+ local_var_path = '/service-areas/{id}'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
118
+
119
+ # query parameters
120
+ query_params = opts[:query_params] || {}
121
+
122
+ # header parameters
123
+ header_params = opts[:header_params] || {}
124
+ # HTTP header 'Accept' (if needed)
125
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
126
+
127
+ # form parameters
128
+ form_params = opts[:form_params] || {}
129
+
130
+ # http body (model)
131
+ post_body = opts[:debug_body]
132
+
133
+ # return_type
134
+ return_type = opts[:debug_return_type] || 'ResponseEnvelope'
135
+
136
+ # auth_names
137
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
138
+
139
+ new_options = opts.merge(
140
+ :operation => :"ServiceAreaApi.delete_service_area",
141
+ :header_params => header_params,
142
+ :query_params => query_params,
143
+ :form_params => form_params,
144
+ :body => post_body,
145
+ :auth_names => auth_names,
146
+ :return_type => return_type
147
+ )
148
+
149
+ data, status_code, headers = @api_client.call_api(:DELETE, local_var_path, new_options)
150
+ if @api_client.config.debugging
151
+ @api_client.config.logger.debug "API called: ServiceAreaApi#delete_service_area\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
152
+ end
153
+ return data, status_code, headers
154
+ end
155
+
22
156
  # Get a service area
23
157
  # Returns one service area — a geographic coverage zone (service territory) the business operates in, with its name and geometry metadata. Reference its UUID as `service_area_id` on customer records for territory-aware dispatch.
24
158
  # @param id [String] Service Area ID
@@ -144,5 +278,79 @@ module Crisphive
144
278
  end
145
279
  return data, status_code, headers
146
280
  end
281
+
282
+ # Adjust a territory's details or its boundary
283
+ # Edits a service area in place, keeping its id and every technician already assigned to it. Partial update: omit a field to keep it, send \"\" to clear an optional text field; `name` rejects \"\". `boundary` is the one to watch: omitting it KEEPS the stored polygon, while sending one REPLACES it outright (no partial merge of geometry). This tool cannot remove a polygon once set. A boundary or postal/city change takes effect for every NEW matching decision (quote checks, confirm, reassign, board moves, slot pickers), including for jobs already on the calendar when they are next re-planned. Jobs already assigned are not re-evaluated automatically, so after moving an edge, re-check listCrewCandidates on upcoming jobs near it.
284
+ # @param id [String] Service Area ID
285
+ # @param service_area_update_request [ServiceAreaUpdateRequest] Service area details
286
+ # @param [Hash] opts the optional parameters
287
+ # @return [ResponseEnvelope]
288
+ def update_service_area(id, service_area_update_request, opts = {})
289
+ data, _status_code, _headers = update_service_area_with_http_info(id, service_area_update_request, opts)
290
+ data
291
+ end
292
+
293
+ # Adjust a territory&#39;s details or its boundary
294
+ # Edits a service area in place, keeping its id and every technician already assigned to it. Partial update: omit a field to keep it, send \&quot;\&quot; to clear an optional text field; &#x60;name&#x60; rejects \&quot;\&quot;. &#x60;boundary&#x60; is the one to watch: omitting it KEEPS the stored polygon, while sending one REPLACES it outright (no partial merge of geometry). This tool cannot remove a polygon once set. A boundary or postal/city change takes effect for every NEW matching decision (quote checks, confirm, reassign, board moves, slot pickers), including for jobs already on the calendar when they are next re-planned. Jobs already assigned are not re-evaluated automatically, so after moving an edge, re-check listCrewCandidates on upcoming jobs near it.
295
+ # @param id [String] Service Area ID
296
+ # @param service_area_update_request [ServiceAreaUpdateRequest] Service area details
297
+ # @param [Hash] opts the optional parameters
298
+ # @return [Array<(ResponseEnvelope, Integer, Hash)>] ResponseEnvelope data, response status code and response headers
299
+ def update_service_area_with_http_info(id, service_area_update_request, opts = {})
300
+ if @api_client.config.debugging
301
+ @api_client.config.logger.debug 'Calling API: ServiceAreaApi.update_service_area ...'
302
+ end
303
+ # verify the required parameter 'id' is set
304
+ if @api_client.config.client_side_validation && id.nil?
305
+ fail ArgumentError, "Missing the required parameter 'id' when calling ServiceAreaApi.update_service_area"
306
+ end
307
+ # verify the required parameter 'service_area_update_request' is set
308
+ if @api_client.config.client_side_validation && service_area_update_request.nil?
309
+ fail ArgumentError, "Missing the required parameter 'service_area_update_request' when calling ServiceAreaApi.update_service_area"
310
+ end
311
+ # resource path
312
+ local_var_path = '/service-areas/{id}'.sub('{' + 'id' + '}', CGI.escape(id.to_s))
313
+
314
+ # query parameters
315
+ query_params = opts[:query_params] || {}
316
+
317
+ # header parameters
318
+ header_params = opts[:header_params] || {}
319
+ # HTTP header 'Accept' (if needed)
320
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
321
+ # HTTP header 'Content-Type'
322
+ content_type = @api_client.select_header_content_type(['application/json'])
323
+ if !content_type.nil?
324
+ header_params['Content-Type'] = content_type
325
+ end
326
+
327
+ # form parameters
328
+ form_params = opts[:form_params] || {}
329
+
330
+ # http body (model)
331
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(service_area_update_request)
332
+
333
+ # return_type
334
+ return_type = opts[:debug_return_type] || 'ResponseEnvelope'
335
+
336
+ # auth_names
337
+ auth_names = opts[:debug_auth_names] || ['ApiKeyAuth']
338
+
339
+ new_options = opts.merge(
340
+ :operation => :"ServiceAreaApi.update_service_area",
341
+ :header_params => header_params,
342
+ :query_params => query_params,
343
+ :form_params => form_params,
344
+ :body => post_body,
345
+ :auth_names => auth_names,
346
+ :return_type => return_type
347
+ )
348
+
349
+ data, status_code, headers = @api_client.call_api(:PUT, local_var_path, new_options)
350
+ if @api_client.config.debugging
351
+ @api_client.config.logger.debug "API called: ServiceAreaApi#update_service_area\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
352
+ end
353
+ return data, status_code, headers
354
+ end
147
355
  end
148
356
  end
@@ -20,7 +20,7 @@ module Crisphive
20
20
  @api_client = api_client
21
21
  end
22
22
  # Add a technician
23
- # Creates a technician membership under the current business. If the phone/email matches an existing user, their account is linked. Otherwise a new user identity is created (no invite email — login is passwordless later). Either way the membership starts active. If the technician was previously removed (deactive) they are reactivated instead. Owner/Administrator groups cannot be assigned via API key, and not at all in sandbox mode. Optional relations (all validated; any missing id → 404 TECHNICIAN_NOT_FOUND with `missing_ids`): `buddy_ids` sets this technician's buddy list (use when creating a lead); `lead_ids` adds this technician as a buddy of each named lead (use when creating a buddy — the buddy-side way to attach the same lead↔buddy relation); `service_area_ids` assigns the technician to those service areas. `start_location_type=office` snapshots the business address + coordinates into the technician at create time; `address`, `start_location_lat`, `start_location_long` in the body are ignored. Requires the business to have coordinates set (else 400 BUSINESS_LOCATION_MISSING). `start_location_type=home` (or empty) uses the address + coordinates from the body.
23
+ # Creates a technician membership under the current business. If the phone/email matches an existing user, their account is linked. Otherwise a new user identity is created. Either way the membership starts active. The new member is notified (live mode only, best-effort): an email when `email` is supplied, an SMS when `phone` is supplied, both when both — informational only, no activation step (login stays passwordless: magic link / OTP). Re-adding someone: if the person is currently SUSPENDED on this business (a dashboard action — not reachable via this API), the create REACTIVATES that existing membership (same technician id, their existing group; also notified). A technician REMOVED with deleteTechnician is a closed membership: re-adding the same email/phone links the SAME underlying person (no duplicate identity) but creates a FRESH membership with a NEW id — history stays under the old one. Owner/Administrator groups cannot be assigned via API key, and not at all in sandbox mode. `assignment_tier` (what the scheduler assigns jobs by) is ignored — stored empty, no error — for the Booking Coordinator, Administrator and Viewer roles, which are never assigned jobs; a reactivated member of those roles also comes back without a tier. Optional relations (all validated; any missing id → 404 TECHNICIAN_NOT_FOUND with `missing_ids`): `buddy_ids` sets this technician's buddy list (use when creating a lead); `lead_ids` adds this technician as a buddy of each named lead (use when creating a buddy — the buddy-side way to attach the same lead↔buddy relation); `service_area_ids` assigns the technician to those service areas. `start_location_type=office` snapshots the business address + coordinates into the technician at create time; `address`, `start_location_lat`, `start_location_long` in the body are ignored. Requires the business to have coordinates set (else 400 BUSINESS_LOCATION_MISSING). `start_location_type=home` (or empty) uses the address + coordinates from the body.
24
24
  # @param technician_create_request [TechnicianCreateRequest] Technician details
25
25
  # @param [Hash] opts the optional parameters
26
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.
@@ -31,7 +31,7 @@ module Crisphive
31
31
  end
32
32
 
33
33
  # Add a technician
34
- # Creates a technician membership under the current business. If the phone/email matches an existing user, their account is linked. Otherwise a new user identity is created (no invite email — login is passwordless later). Either way the membership starts active. If the technician was previously removed (deactive) they are reactivated instead. Owner/Administrator groups cannot be assigned via API key, and not at all in sandbox mode. Optional relations (all validated; any missing id → 404 TECHNICIAN_NOT_FOUND with &#x60;missing_ids&#x60;): &#x60;buddy_ids&#x60; sets this technician&#39;s buddy list (use when creating a lead); &#x60;lead_ids&#x60; adds this technician as a buddy of each named lead (use when creating a buddy — the buddy-side way to attach the same lead↔buddy relation); &#x60;service_area_ids&#x60; assigns the technician to those service areas. &#x60;start_location_type&#x3D;office&#x60; snapshots the business address + coordinates into the technician at create time; &#x60;address&#x60;, &#x60;start_location_lat&#x60;, &#x60;start_location_long&#x60; in the body are ignored. Requires the business to have coordinates set (else 400 BUSINESS_LOCATION_MISSING). &#x60;start_location_type&#x3D;home&#x60; (or empty) uses the address + coordinates from the body.
34
+ # Creates a technician membership under the current business. If the phone/email matches an existing user, their account is linked. Otherwise a new user identity is created. Either way the membership starts active. The new member is notified (live mode only, best-effort): an email when &#x60;email&#x60; is supplied, an SMS when &#x60;phone&#x60; is supplied, both when both — informational only, no activation step (login stays passwordless: magic link / OTP). Re-adding someone: if the person is currently SUSPENDED on this business (a dashboard action — not reachable via this API), the create REACTIVATES that existing membership (same technician id, their existing group; also notified). A technician REMOVED with deleteTechnician is a closed membership: re-adding the same email/phone links the SAME underlying person (no duplicate identity) but creates a FRESH membership with a NEW id — history stays under the old one. Owner/Administrator groups cannot be assigned via API key, and not at all in sandbox mode. &#x60;assignment_tier&#x60; (what the scheduler assigns jobs by) is ignored — stored empty, no error — for the Booking Coordinator, Administrator and Viewer roles, which are never assigned jobs; a reactivated member of those roles also comes back without a tier. Optional relations (all validated; any missing id → 404 TECHNICIAN_NOT_FOUND with &#x60;missing_ids&#x60;): &#x60;buddy_ids&#x60; sets this technician&#39;s buddy list (use when creating a lead); &#x60;lead_ids&#x60; adds this technician as a buddy of each named lead (use when creating a buddy — the buddy-side way to attach the same lead↔buddy relation); &#x60;service_area_ids&#x60; assigns the technician to those service areas. &#x60;start_location_type&#x3D;office&#x60; snapshots the business address + coordinates into the technician at create time; &#x60;address&#x60;, &#x60;start_location_lat&#x60;, &#x60;start_location_long&#x60; in the body are ignored. Requires the business to have coordinates set (else 400 BUSINESS_LOCATION_MISSING). &#x60;start_location_type&#x3D;home&#x60; (or empty) uses the address + coordinates from the body.
35
35
  # @param technician_create_request [TechnicianCreateRequest] Technician details
36
36
  # @param [Hash] opts the optional parameters
37
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.
@@ -90,8 +90,8 @@ module Crisphive
90
90
  return data, status_code, headers
91
91
  end
92
92
 
93
- # Remove a technician
94
- # Soft-removes a technician from the business by setting status to deactive
93
+ # Remove a technician from the business
94
+ # Closes the technician's membership: the profile is set to deactive and soft-deleted, and their access to this business ends on their next request. In the same operation they are removed from every other technician's buddy list, vehicles they own are released, and any personal-calendar connection they made for this business is revoked. Fires the technician.deleted webhook. Their underlying user identity is untouched, and so are memberships at other businesses. Removal is REFUSED while the member is still staffed on an open job, as the lead or through a crew session, where open means not completed, not archived and not deleted (409 TECHNICIAN_HAS_OPEN_JOBS; nothing is written). data.open_job_count is the exact count and is always present. data.jobs lists up to 20 of them (id, short_code, current_status_key, scheduled_at when confirmed) ONLY when the caller also holds job_manage (a credential that may manage the team but not the job board gets the count alone; the key is then absent). Re-staff them first: reassign each with listCrewCandidates, re-plan whole days with previewAbsenceResolve / commitAbsenceResolve (which needs a time-off record covering those days; create one with createTechnicianTimeOff), or, from the dashboard, complete or archive the job. To cut someone's access immediately while their work is re-staffed, suspend them from the dashboard: suspension is not gated. This is a closed membership, not a pause. Re-adding the same email or phone later with createTechnician links the SAME person but opens a FRESH membership with a NEW technician id and none of the old buddy, vehicle or area links. If you expect the person back, suspend them from the dashboard instead; a suspended member is reactivated in place by createTechnician, keeping their id and group. The last active Owner cannot be removed (TECHNICIAN_LAST_OWNER). API keys the person created are NOT revoked (an HR action must not take an integration down); if any are still active, the business Owners and Administrators are emailed a list of them.
95
95
  # @param id [String] Technician ID
96
96
  # @param [Hash] opts the optional parameters
97
97
  # @return [ResponseEnvelope]
@@ -100,8 +100,8 @@ module Crisphive
100
100
  data
101
101
  end
102
102
 
103
- # Remove a technician
104
- # Soft-removes a technician from the business by setting status to deactive
103
+ # Remove a technician from the business
104
+ # Closes the technician&#39;s membership: the profile is set to deactive and soft-deleted, and their access to this business ends on their next request. In the same operation they are removed from every other technician&#39;s buddy list, vehicles they own are released, and any personal-calendar connection they made for this business is revoked. Fires the technician.deleted webhook. Their underlying user identity is untouched, and so are memberships at other businesses. Removal is REFUSED while the member is still staffed on an open job, as the lead or through a crew session, where open means not completed, not archived and not deleted (409 TECHNICIAN_HAS_OPEN_JOBS; nothing is written). data.open_job_count is the exact count and is always present. data.jobs lists up to 20 of them (id, short_code, current_status_key, scheduled_at when confirmed) ONLY when the caller also holds job_manage (a credential that may manage the team but not the job board gets the count alone; the key is then absent). Re-staff them first: reassign each with listCrewCandidates, re-plan whole days with previewAbsenceResolve / commitAbsenceResolve (which needs a time-off record covering those days; create one with createTechnicianTimeOff), or, from the dashboard, complete or archive the job. To cut someone&#39;s access immediately while their work is re-staffed, suspend them from the dashboard: suspension is not gated. This is a closed membership, not a pause. Re-adding the same email or phone later with createTechnician links the SAME person but opens a FRESH membership with a NEW technician id and none of the old buddy, vehicle or area links. If you expect the person back, suspend them from the dashboard instead; a suspended member is reactivated in place by createTechnician, keeping their id and group. The last active Owner cannot be removed (TECHNICIAN_LAST_OWNER). API keys the person created are NOT revoked (an HR action must not take an integration down); if any are still active, the business Owners and Administrators are emailed a list of them.
105
105
  # @param id [String] Technician ID
106
106
  # @param [Hash] opts the optional parameters
107
107
  # @return [Array<(ResponseEnvelope, Integer, Hash)>] ResponseEnvelope data, response status code and response headers
@@ -591,7 +591,7 @@ module Crisphive
591
591
  end
592
592
 
593
593
  # Update a technician
594
- # Updates mutable technician profile fields. `start_location_type=office` re-snapshots the current business address + coordinates (body `address`/`start_location_lat`/`start_location_long` ignored; requires business coordinates, else 400 BUSINESS_LOCATION_MISSING). `start_location_type=home` (or empty) uses the address + coordinates from the body.
594
+ # Updates mutable technician profile fields. `assignment_tier` is ignored (stored empty, no error) for the Booking Coordinator, Administrator and Viewer roles, so moving a member into one of them clears the tier and frees their technician seat. `start_location_type=office` re-snapshots the current business address + coordinates (body `address`/`start_location_lat`/`start_location_long` ignored; requires business coordinates, else 400 BUSINESS_LOCATION_MISSING). `start_location_type=home` (or empty) uses the address + coordinates from the body.
595
595
  # @param id [String] Technician ID
596
596
  # @param technician_update_request [TechnicianUpdateRequest] Technician details
597
597
  # @param [Hash] opts the optional parameters
@@ -602,7 +602,7 @@ module Crisphive
602
602
  end
603
603
 
604
604
  # Update a technician
605
- # Updates mutable technician profile fields. &#x60;start_location_type&#x3D;office&#x60; re-snapshots the current business address + coordinates (body &#x60;address&#x60;/&#x60;start_location_lat&#x60;/&#x60;start_location_long&#x60; ignored; requires business coordinates, else 400 BUSINESS_LOCATION_MISSING). &#x60;start_location_type&#x3D;home&#x60; (or empty) uses the address + coordinates from the body.
605
+ # Updates mutable technician profile fields. &#x60;assignment_tier&#x60; is ignored (stored empty, no error) for the Booking Coordinator, Administrator and Viewer roles, so moving a member into one of them clears the tier and frees their technician seat. &#x60;start_location_type&#x3D;office&#x60; re-snapshots the current business address + coordinates (body &#x60;address&#x60;/&#x60;start_location_lat&#x60;/&#x60;start_location_long&#x60; ignored; requires business coordinates, else 400 BUSINESS_LOCATION_MISSING). &#x60;start_location_type&#x3D;home&#x60; (or empty) uses the address + coordinates from the body.
606
606
  # @param id [String] Technician ID
607
607
  # @param technician_update_request [TechnicianUpdateRequest] Technician details
608
608
  # @param [Hash] opts the optional parameters