wxcli 1.2.0__py3-none-any.whl

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 (413) hide show
  1. wxcli/__init__.py +4 -0
  2. wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
  3. wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
  4. wxcli/_playbook/.claude/rules/cleanup.md +29 -0
  5. wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
  6. wxcli/_playbook/.claude/rules/org-health.md +14 -0
  7. wxcli/_playbook/.claude/settings.json +8 -0
  8. wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
  9. wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
  10. wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
  11. wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
  12. wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
  13. wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
  14. wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
  15. wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
  16. wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
  17. wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
  18. wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
  19. wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
  20. wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
  21. wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
  22. wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
  23. wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
  24. wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
  25. wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
  26. wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
  27. wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
  28. wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
  29. wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
  30. wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
  31. wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
  32. wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
  33. wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
  34. wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
  35. wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
  36. wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
  37. wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
  38. wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
  39. wxcli/_playbook/CLAUDE.md +373 -0
  40. wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
  41. wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
  42. wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
  43. wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
  44. wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
  45. wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
  46. wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
  47. wxcli/_playbook/docs/reference/admin-partner.md +357 -0
  48. wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
  49. wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
  50. wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
  51. wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
  52. wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
  53. wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
  54. wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
  55. wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
  56. wxcli/_playbook/docs/reference/authentication.md +965 -0
  57. wxcli/_playbook/docs/reference/call-control.md +1205 -0
  58. wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
  59. wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
  60. wxcli/_playbook/docs/reference/call-routing.md +2304 -0
  61. wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
  62. wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
  63. wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
  64. wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
  65. wxcli/_playbook/docs/reference/devices-core.md +1869 -0
  66. wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
  67. wxcli/_playbook/docs/reference/devices-platform.md +467 -0
  68. wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
  69. wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
  70. wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
  71. wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
  72. wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
  73. wxcli/_playbook/docs/reference/meetings-content.md +473 -0
  74. wxcli/_playbook/docs/reference/meetings-core.md +903 -0
  75. wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
  76. wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
  77. wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
  78. wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
  79. wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
  80. wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
  81. wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
  82. wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
  83. wxcli/_playbook/docs/reference/provisioning.md +1366 -0
  84. wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
  85. wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
  86. wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
  87. wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
  88. wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
  89. wxcli/_version.py +24 -0
  90. wxcli/auth.py +137 -0
  91. wxcli/commands/__init__.py +0 -0
  92. wxcli/commands/_registry.py +181 -0
  93. wxcli/commands/activation_email.py +103 -0
  94. wxcli/commands/admin_recordings.py +585 -0
  95. wxcli/commands/analytics.py +105 -0
  96. wxcli/commands/announcement_playlists.py +219 -0
  97. wxcli/commands/announcements.py +519 -0
  98. wxcli/commands/archive_users.py +36 -0
  99. wxcli/commands/attachment_actions.py +60 -0
  100. wxcli/commands/audit_events.py +83 -0
  101. wxcli/commands/authorizations.py +113 -0
  102. wxcli/commands/auto_attendant.py +642 -0
  103. wxcli/commands/broadworks_billing_reports.py +126 -0
  104. wxcli/commands/broadworks_enterprises.py +160 -0
  105. wxcli/commands/broadworks_subscribers.py +319 -0
  106. wxcli/commands/broadworks_workspaces.py +108 -0
  107. wxcli/commands/call_controls.py +1304 -0
  108. wxcli/commands/call_park.py +494 -0
  109. wxcli/commands/call_pickup.py +229 -0
  110. wxcli/commands/call_queue.py +1878 -0
  111. wxcli/commands/call_recording.py +707 -0
  112. wxcli/commands/call_routing.py +1610 -0
  113. wxcli/commands/call_settings_for_me_phase_5.py +212 -0
  114. wxcli/commands/caller_reputation.py +169 -0
  115. wxcli/commands/calling_service.py +305 -0
  116. wxcli/commands/cc_address_book.py +770 -0
  117. wxcli/commands/cc_agent_greetings.py +242 -0
  118. wxcli/commands/cc_agent_summaries.py +39 -0
  119. wxcli/commands/cc_agent_wellbeing.py +216 -0
  120. wxcli/commands/cc_agents.py +425 -0
  121. wxcli/commands/cc_ai_assistant.py +55 -0
  122. wxcli/commands/cc_ai_feature.py +294 -0
  123. wxcli/commands/cc_audio_files.py +149 -0
  124. wxcli/commands/cc_auto_csat.py +311 -0
  125. wxcli/commands/cc_aux_code.py +458 -0
  126. wxcli/commands/cc_business_hour.py +353 -0
  127. wxcli/commands/cc_call_monitoring.py +228 -0
  128. wxcli/commands/cc_callbacks.py +194 -0
  129. wxcli/commands/cc_campaign.py +212 -0
  130. wxcli/commands/cc_captures.py +39 -0
  131. wxcli/commands/cc_contact_list.py +169 -0
  132. wxcli/commands/cc_contact_number.py +295 -0
  133. wxcli/commands/cc_data_sources.py +235 -0
  134. wxcli/commands/cc_desktop_layout.py +380 -0
  135. wxcli/commands/cc_desktop_profile.py +322 -0
  136. wxcli/commands/cc_dial_number.py +511 -0
  137. wxcli/commands/cc_dial_plan.py +347 -0
  138. wxcli/commands/cc_dnc.py +101 -0
  139. wxcli/commands/cc_entry_point.py +545 -0
  140. wxcli/commands/cc_ewt.py +51 -0
  141. wxcli/commands/cc_flow.py +477 -0
  142. wxcli/commands/cc_global_vars.py +430 -0
  143. wxcli/commands/cc_holiday_list.py +335 -0
  144. wxcli/commands/cc_journey.py +1261 -0
  145. wxcli/commands/cc_legacy_flows.py +49 -0
  146. wxcli/commands/cc_multimedia_profile.py +436 -0
  147. wxcli/commands/cc_notification.py +51 -0
  148. wxcli/commands/cc_outdial_ani.py +604 -0
  149. wxcli/commands/cc_overrides.py +341 -0
  150. wxcli/commands/cc_queue.py +1083 -0
  151. wxcli/commands/cc_queue_stats.py +55 -0
  152. wxcli/commands/cc_realtime.py +51 -0
  153. wxcli/commands/cc_resource_collection.py +312 -0
  154. wxcli/commands/cc_search.py +46 -0
  155. wxcli/commands/cc_site.py +400 -0
  156. wxcli/commands/cc_skill.py +350 -0
  157. wxcli/commands/cc_skill_profile.py +289 -0
  158. wxcli/commands/cc_subscriptions.py +426 -0
  159. wxcli/commands/cc_summaries.py +130 -0
  160. wxcli/commands/cc_tasks.py +844 -0
  161. wxcli/commands/cc_team.py +325 -0
  162. wxcli/commands/cc_user_profiles.py +349 -0
  163. wxcli/commands/cc_users.py +701 -0
  164. wxcli/commands/cc_work_types.py +363 -0
  165. wxcli/commands/cdr.py +89 -0
  166. wxcli/commands/classifications.py +38 -0
  167. wxcli/commands/cleanup.py +1427 -0
  168. wxcli/commands/client_settings.py +72 -0
  169. wxcli/commands/conference.py +328 -0
  170. wxcli/commands/configure.py +72 -0
  171. wxcli/commands/converged_recordings.py +347 -0
  172. wxcli/commands/converged_recordings_export.py +310 -0
  173. wxcli/commands/cq_playlists.py +42 -0
  174. wxcli/commands/cucm.py +2794 -0
  175. wxcli/commands/cucm_config.py +129 -0
  176. wxcli/commands/customer_assist.py +373 -0
  177. wxcli/commands/data_sources.py +227 -0
  178. wxcli/commands/dect_devices.py +787 -0
  179. wxcli/commands/device_configurations.py +77 -0
  180. wxcli/commands/device_dynamic_settings.py +320 -0
  181. wxcli/commands/device_settings.py +1489 -0
  182. wxcli/commands/devices.py +280 -0
  183. wxcli/commands/domains.py +142 -0
  184. wxcli/commands/ecm.py +178 -0
  185. wxcli/commands/emergency_services.py +827 -0
  186. wxcli/commands/events.py +83 -0
  187. wxcli/commands/external_voicemail.py +52 -0
  188. wxcli/commands/groups.py +206 -0
  189. wxcli/commands/guest_management.py +76 -0
  190. wxcli/commands/hds.py +340 -0
  191. wxcli/commands/hot_desk.py +67 -0
  192. wxcli/commands/hot_desking_members.py +118 -0
  193. wxcli/commands/hot_desking_portal.py +124 -0
  194. wxcli/commands/hunt_group.py +577 -0
  195. wxcli/commands/hybrid_clusters.py +71 -0
  196. wxcli/commands/hybrid_connectors.py +67 -0
  197. wxcli/commands/identity_org.py +159 -0
  198. wxcli/commands/init_playbook.py +155 -0
  199. wxcli/commands/licenses.py +106 -0
  200. wxcli/commands/live_monitoring.py +40 -0
  201. wxcli/commands/location_call_handling.py +577 -0
  202. wxcli/commands/location_schedules.py +341 -0
  203. wxcli/commands/location_settings.py +1297 -0
  204. wxcli/commands/location_voicemail.py +494 -0
  205. wxcli/commands/locations.py +336 -0
  206. wxcli/commands/meeting_captions.py +110 -0
  207. wxcli/commands/meeting_chats.py +63 -0
  208. wxcli/commands/meeting_invitees.py +234 -0
  209. wxcli/commands/meeting_messages.py +28 -0
  210. wxcli/commands/meeting_participants.py +278 -0
  211. wxcli/commands/meeting_polls.py +111 -0
  212. wxcli/commands/meeting_preferences.py +515 -0
  213. wxcli/commands/meeting_qa.py +78 -0
  214. wxcli/commands/meeting_qualities.py +43 -0
  215. wxcli/commands/meeting_reports.py +101 -0
  216. wxcli/commands/meeting_session_types.py +105 -0
  217. wxcli/commands/meeting_site.py +57 -0
  218. wxcli/commands/meeting_slido.py +44 -0
  219. wxcli/commands/meeting_summaries.py +92 -0
  220. wxcli/commands/meeting_tracking_codes.py +233 -0
  221. wxcli/commands/meeting_transcripts.py +230 -0
  222. wxcli/commands/meetings.py +1997 -0
  223. wxcli/commands/memberships.py +164 -0
  224. wxcli/commands/messages.py +213 -0
  225. wxcli/commands/mode_management.py +244 -0
  226. wxcli/commands/my_call_settings.py +3577 -0
  227. wxcli/commands/numbers.py +405 -0
  228. wxcli/commands/operating_modes.py +410 -0
  229. wxcli/commands/org_contacts.py +284 -0
  230. wxcli/commands/org_health_cli.py +55 -0
  231. wxcli/commands/org_settings.py +71 -0
  232. wxcli/commands/organizations.py +83 -0
  233. wxcli/commands/paging_group.py +257 -0
  234. wxcli/commands/partner_admins.py +141 -0
  235. wxcli/commands/partner_reports.py +188 -0
  236. wxcli/commands/partner_tags.py +216 -0
  237. wxcli/commands/people.py +279 -0
  238. wxcli/commands/person_call_settings.py +71 -0
  239. wxcli/commands/pstn.py +280 -0
  240. wxcli/commands/recording_report.py +154 -0
  241. wxcli/commands/report_templates.py +38 -0
  242. wxcli/commands/reports.py +144 -0
  243. wxcli/commands/resource_group_memberships.py +160 -0
  244. wxcli/commands/resource_groups.py +67 -0
  245. wxcli/commands/roles.py +63 -0
  246. wxcli/commands/room_tabs.py +160 -0
  247. wxcli/commands/rooms.py +225 -0
  248. wxcli/commands/scim_bulk.py +46 -0
  249. wxcli/commands/scim_groups.py +246 -0
  250. wxcli/commands/scim_schemas.py +82 -0
  251. wxcli/commands/scim_users.py +283 -0
  252. wxcli/commands/security_audit.py +54 -0
  253. wxcli/commands/service_apps.py +51 -0
  254. wxcli/commands/single_number_reach.py +209 -0
  255. wxcli/commands/team_memberships.py +155 -0
  256. wxcli/commands/teams.py +153 -0
  257. wxcli/commands/ucm_profile.py +42 -0
  258. wxcli/commands/update.py +219 -0
  259. wxcli/commands/user_settings.py +5116 -0
  260. wxcli/commands/video_mesh.py +1090 -0
  261. wxcli/commands/virtual_extensions.py +502 -0
  262. wxcli/commands/virtual_line_settings.py +2072 -0
  263. wxcli/commands/webhooks.py +176 -0
  264. wxcli/commands/wholesale_billing_reports.py +147 -0
  265. wxcli/commands/wholesale_provisioning.py +523 -0
  266. wxcli/commands/workspace_locations.py +339 -0
  267. wxcli/commands/workspace_metrics.py +103 -0
  268. wxcli/commands/workspace_personalization.py +70 -0
  269. wxcli/commands/workspace_settings.py +3347 -0
  270. wxcli/commands/workspaces.py +279 -0
  271. wxcli/commands/xapi.py +114 -0
  272. wxcli/config.py +139 -0
  273. wxcli/errors.py +77 -0
  274. wxcli/main.py +210 -0
  275. wxcli/migration/CLAUDE.md +106 -0
  276. wxcli/migration/__init__.py +1 -0
  277. wxcli/migration/advisory/CLAUDE.md +196 -0
  278. wxcli/migration/advisory/__init__.py +46 -0
  279. wxcli/migration/advisory/advisor.py +107 -0
  280. wxcli/migration/advisory/advisory_patterns.py +2552 -0
  281. wxcli/migration/advisory/recommendation_rules.py +835 -0
  282. wxcli/migration/cucm/CLAUDE.md +74 -0
  283. wxcli/migration/cucm/__init__.py +5 -0
  284. wxcli/migration/cucm/connection.py +265 -0
  285. wxcli/migration/cucm/discovery.py +225 -0
  286. wxcli/migration/cucm/extractors/__init__.py +1 -0
  287. wxcli/migration/cucm/extractors/announcements.py +92 -0
  288. wxcli/migration/cucm/extractors/base.py +90 -0
  289. wxcli/migration/cucm/extractors/device_profiles.py +98 -0
  290. wxcli/migration/cucm/extractors/devices.py +193 -0
  291. wxcli/migration/cucm/extractors/e911.py +103 -0
  292. wxcli/migration/cucm/extractors/features.py +309 -0
  293. wxcli/migration/cucm/extractors/helpers.py +73 -0
  294. wxcli/migration/cucm/extractors/informational.py +295 -0
  295. wxcli/migration/cucm/extractors/locations.py +221 -0
  296. wxcli/migration/cucm/extractors/moh.py +100 -0
  297. wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
  298. wxcli/migration/cucm/extractors/routing.py +493 -0
  299. wxcli/migration/cucm/extractors/shared_lines.py +146 -0
  300. wxcli/migration/cucm/extractors/templates.py +229 -0
  301. wxcli/migration/cucm/extractors/tier4.py +173 -0
  302. wxcli/migration/cucm/extractors/users.py +240 -0
  303. wxcli/migration/cucm/extractors/voicemail.py +243 -0
  304. wxcli/migration/cucm/extractors/workspaces.py +88 -0
  305. wxcli/migration/cucm/unity_connection.py +304 -0
  306. wxcli/migration/execute/CLAUDE.md +372 -0
  307. wxcli/migration/execute/__init__.py +303 -0
  308. wxcli/migration/execute/batch.py +277 -0
  309. wxcli/migration/execute/dependency.py +483 -0
  310. wxcli/migration/execute/engine.py +964 -0
  311. wxcli/migration/execute/handlers.py +2232 -0
  312. wxcli/migration/execute/planner.py +1939 -0
  313. wxcli/migration/execute/runtime.py +571 -0
  314. wxcli/migration/export/CLAUDE.md +49 -0
  315. wxcli/migration/export/__init__.py +8 -0
  316. wxcli/migration/export/csv_export.py +126 -0
  317. wxcli/migration/export/deployment_plan.py +479 -0
  318. wxcli/migration/export/json_export.py +60 -0
  319. wxcli/migration/models.py +941 -0
  320. wxcli/migration/phone_models.py +50 -0
  321. wxcli/migration/preflight/CLAUDE.md +52 -0
  322. wxcli/migration/preflight/__init__.py +89 -0
  323. wxcli/migration/preflight/checks.py +911 -0
  324. wxcli/migration/preflight/runner.py +293 -0
  325. wxcli/migration/rate_limiter.py +144 -0
  326. wxcli/migration/report/CLAUDE.md +245 -0
  327. wxcli/migration/report/__init__.py +7 -0
  328. wxcli/migration/report/appendix.py +2437 -0
  329. wxcli/migration/report/assembler.py +183 -0
  330. wxcli/migration/report/charts.py +263 -0
  331. wxcli/migration/report/executive.py +776 -0
  332. wxcli/migration/report/explainer.py +704 -0
  333. wxcli/migration/report/helpers.py +74 -0
  334. wxcli/migration/report/ingest.py +152 -0
  335. wxcli/migration/report/notice_templates.py +192 -0
  336. wxcli/migration/report/score.py +375 -0
  337. wxcli/migration/report/styles.py +1052 -0
  338. wxcli/migration/report/user_diff.py +1052 -0
  339. wxcli/migration/report/user_notice.py +425 -0
  340. wxcli/migration/state.py +139 -0
  341. wxcli/migration/store.py +860 -0
  342. wxcli/migration/transform/CLAUDE.md +107 -0
  343. wxcli/migration/transform/__init__.py +12 -0
  344. wxcli/migration/transform/analysis_pipeline.py +453 -0
  345. wxcli/migration/transform/analyzers/__init__.py +142 -0
  346. wxcli/migration/transform/analyzers/css_permission.py +189 -0
  347. wxcli/migration/transform/analyzers/css_routing.py +335 -0
  348. wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
  349. wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
  350. wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
  351. wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
  352. wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
  353. wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
  354. wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
  355. wxcli/migration/transform/analyzers/missing_data.py +300 -0
  356. wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
  357. wxcli/migration/transform/analyzers/shared_line.py +170 -0
  358. wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
  359. wxcli/migration/transform/analyzers/workspace_license.py +212 -0
  360. wxcli/migration/transform/cross_reference.py +1136 -0
  361. wxcli/migration/transform/cucm_pattern.py +246 -0
  362. wxcli/migration/transform/decisions.py +301 -0
  363. wxcli/migration/transform/e164.py +145 -0
  364. wxcli/migration/transform/engine.py +256 -0
  365. wxcli/migration/transform/mappers/CLAUDE.md +123 -0
  366. wxcli/migration/transform/mappers/__init__.py +76 -0
  367. wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
  368. wxcli/migration/transform/mappers/base.py +190 -0
  369. wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
  370. wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
  371. wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
  372. wxcli/migration/transform/mappers/css_mapper.py +868 -0
  373. wxcli/migration/transform/mappers/dect_mapper.py +367 -0
  374. wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
  375. wxcli/migration/transform/mappers/device_mapper.py +227 -0
  376. wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
  377. wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
  378. wxcli/migration/transform/mappers/e911_mapper.py +129 -0
  379. wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
  380. wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
  381. wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
  382. wxcli/migration/transform/mappers/line_mapper.py +310 -0
  383. wxcli/migration/transform/mappers/location_mapper.py +235 -0
  384. wxcli/migration/transform/mappers/moh_mapper.py +114 -0
  385. wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
  386. wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
  387. wxcli/migration/transform/mappers/routing_mapper.py +821 -0
  388. wxcli/migration/transform/mappers/snr_mapper.py +201 -0
  389. wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
  390. wxcli/migration/transform/mappers/user_mapper.py +264 -0
  391. wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
  392. wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
  393. wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
  394. wxcli/migration/transform/normalizers.py +2008 -0
  395. wxcli/migration/transform/pattern_converter.py +106 -0
  396. wxcli/migration/transform/pipeline.py +271 -0
  397. wxcli/migration/transform/rules.py +255 -0
  398. wxcli/org_health/CLAUDE.md +63 -0
  399. wxcli/org_health/__init__.py +7 -0
  400. wxcli/org_health/analyze.py +93 -0
  401. wxcli/org_health/checks.py +458 -0
  402. wxcli/org_health/collector.py +62 -0
  403. wxcli/org_health/models.py +78 -0
  404. wxcli/org_health/report.py +300 -0
  405. wxcli/output.py +101 -0
  406. wxcli-1.2.0.dist-info/METADATA +505 -0
  407. wxcli-1.2.0.dist-info/RECORD +413 -0
  408. wxcli-1.2.0.dist-info/WHEEL +5 -0
  409. wxcli-1.2.0.dist-info/entry_points.txt +2 -0
  410. wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
  411. wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
  412. wxcli-1.2.0.dist-info/scm_version.json +8 -0
  413. wxcli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1482 @@
1
+ <!-- Updated by playbook session 2026-03-18 -->
2
+ # Location Recording, Supervisor, Guest Calling, Conference & Misc
3
+
4
+ **CLI group:** `wxcli call-recording` (org-level recording vendor, compliance, per-location vendor assignment)
5
+ **NOT:** `wxcli location-call-settings` (that group handles dial patterns for premise PSTN — completely different domain)
6
+
7
+ ## Sources
8
+
9
+ - wxc_sdk v1.30.0
10
+ - OpenAPI spec: specs/webex-cloud-calling.json
11
+ - developer.webex.com Location Call Settings APIs
12
+
13
+ Reference for advanced location-level and org-level call settings managed through the wxc_sdk. Covers call recording vendors and compliance, caller reputation (spam scoring), conference controls, supervisor/agent management, guest calling (click-to-call), operating modes, hot desking via voice portal, and shared forwarding patterns used across features.
14
+
15
+ ---
16
+
17
+ ## Table of Contents
18
+
19
+ 1. [Call Recording](#1-call-recording)
20
+ 2. [Caller Reputation](#2-caller-reputation)
21
+ 3. [Conference Controls](#3-conference-controls)
22
+ 4. [Supervisors](#4-supervisors)
23
+ 5. [Guest Calling (Click-to-Call)](#5-guest-calling-click-to-call)
24
+ 6. [Operating Modes](#6-operating-modes)
25
+ 7. [Hot Desking Sign-in via Voice Portal](#7-hot-desking-sign-in-via-voice-portal)
26
+ 8. [Forwarding (Shared Patterns)](#8-forwarding-shared-patterns)
27
+
28
+ ---
29
+
30
+ ## 1. Call Recording
31
+
32
+ **SDK module:** `wxc_sdk.telephony.call_recording`
33
+ **API class:** `CallRecordingSettingsApi` (base: `telephony/config`)
34
+ **Not supported** for Webex for Government (FedRAMP). See [authentication.md → FedRAMP](authentication.md#webex-for-government-fedramp) for all FedRAMP restrictions.
35
+
36
+ Call recording supports multiple third-party vendors. The org has an overall default vendor, but individual locations can override to a different vendor.
37
+
38
+ ### 1.1 Scopes
39
+
40
+ | Operation | Scope |
41
+ |-----------|-------|
42
+ | Read | `spark-admin:telephony_config_read` |
43
+ | Write | `spark-admin:telephony_config_write` |
44
+
45
+ ### 1.2 Data Models
46
+
47
+ #### `CallRecordingInfo`
48
+ Top-level org recording status.
49
+
50
+ | Field | Type | Notes |
51
+ |-------|------|-------|
52
+ | `organization` | `IdAndName` | Org id and name |
53
+ | `enabled` | `bool` | Whether call recording is enabled |
54
+ | `vendor_id` | `str` | Current vendor ID |
55
+ | `vendor_name` | `str` | Current vendor name |
56
+ | `terms_of_service_url` | `str` | Empty for Webex Recording Platform |
57
+
58
+ #### `CallRecordingTermsOfService`
59
+
60
+ | Field | Type | Notes |
61
+ |-------|------|-------|
62
+ | `vendor_id` | `str` | |
63
+ | `vendor_name` | `str` | |
64
+ | `terms_of_service_enabled` | `bool` | |
65
+ | `terms_of_service_url` | `str` | Empty for Webex Recording Platform |
66
+
67
+ #### `OrgComplianceAnnouncement`
68
+ Controls whether the recording START/STOP announcement plays on PSTN calls.
69
+
70
+ | Field | Type | Notes |
71
+ |-------|------|-------|
72
+ | `inbound_pstncalls_enabled` | `bool` | Alias: `inboundPSTNCallsEnabled` |
73
+ | `outbound_pstncalls_enabled` | `bool` | Alias: `outboundPSTNCallsEnabled` |
74
+ | `outbound_pstncalls_delay_enabled` | `bool` | Alias: `outboundPSTNCallsDelayEnabled` |
75
+ | `delay_in_seconds` | `int` | Seconds before announcement plays |
76
+
77
+ **Interaction note:** When the compliance announcement plays to the PSTN party and that party is connected to someone with call recording enabled, the start/stop announcement is inhibited.
78
+
79
+ #### `LocationComplianceAnnouncement` (extends `OrgComplianceAnnouncement`)
80
+
81
+ | Additional Field | Type | Notes |
82
+ |------------------|------|-------|
83
+ | `use_org_settings_enabled` | `bool` | Use org-level defaults |
84
+
85
+ #### `FailureBehavior` (Enum)
86
+ What happens when recording fails:
87
+
88
+ | Value | Behavior |
89
+ |-------|----------|
90
+ | `PROCEED_WITH_CALL_NO_ANNOUNCEMENT` | Call continues, no announcement |
91
+ | `PROCEED_CALL_WITH_ANNOUNCEMENT` | Call continues, announcement plays |
92
+ | `END_CALL_WITH_ANNOUNCEMENT` | Call ends with announcement |
93
+
94
+ #### `RecordingVendor`
95
+
96
+ | Field | Type |
97
+ |-------|------|
98
+ | `id` | `str` |
99
+ | `name` | `str` |
100
+ | `description` | `str` |
101
+ | `migrate_user_creation_enabled` | `bool` |
102
+ | `login_url` | `str` |
103
+ | `terms_of_service_url` | `str` |
104
+
105
+ #### `CallRecordingVendors` (org-level)
106
+
107
+ | Field | Type | Notes |
108
+ |-------|------|-------|
109
+ | `vendor_id` | `str` | Current vendor |
110
+ | `vendor_name` | `str` | |
111
+ | `vendors` | `list[RecordingVendor]` | All available vendors |
112
+ | `storage_region` | `str` | Only for Webex vendor |
113
+ | `failure_behavior` | `FailureBehavior` | |
114
+
115
+ #### `CallRecordingLocationVendors`
116
+
117
+ | Field | Type | Notes |
118
+ |-------|------|-------|
119
+ | `org_default_enabled` | `bool` | |
120
+ | `org_default_vendor_id` | `str` | |
121
+ | `org_default_vendor_name` | `str` | |
122
+ | `default_vendor_id` | `str` | Location-level vendor |
123
+ | `default_vendor_name` | `str` | |
124
+ | `vendors` | `list[RecordingVendor]` | |
125
+ | `org_storage_region_enabled` | `bool` | |
126
+ | `org_storage_region` | `str` | |
127
+ | `storage_region` | `str` | Location-level region |
128
+ | `org_failure_behavior_enabled` | `bool` | |
129
+ | `org_failure_behavior` | `FailureBehavior` | |
130
+ | `failure_behavior` | `FailureBehavior` | Location-level |
131
+
132
+ #### `CallRecordingRegion`
133
+
134
+ | Field | Type |
135
+ |-------|------|
136
+ | `code` | `str` — two-character region code |
137
+ | `name` | `str` |
138
+ | `default_enabled` | `bool` |
139
+
140
+ #### `RecordingUser`
141
+
142
+ | Field | Type |
143
+ |-------|------|
144
+ | `id` | `str` |
145
+ | `first_name` | `str` |
146
+ | `last_name` | `str` |
147
+ | `type` | `UserType` |
148
+ | `license_type` | `UserLicenseType` |
149
+
150
+ ### 1.3 API Methods
151
+
152
+ ```python
153
+ class CallRecordingSettingsApi:
154
+
155
+ # --- Org-level recording on/off ---
156
+ def read(self, org_id: str = None) -> CallRecordingInfo
157
+ def update(self, enabled: bool, org_id: str = None) # Cisco partners only
158
+
159
+ # --- Terms of service ---
160
+ def read_terms_of_service(self, vendor_id: str, org_id: str = None) -> CallRecordingTermsOfService
161
+ def update_terms_of_service(self, vendor_id: str, enabled: bool, org_id: str = None)
162
+
163
+ # --- Compliance announcement (org) ---
164
+ def read_org_compliance_announcement(self, org_id: str = None) -> OrgComplianceAnnouncement
165
+ def update_org_compliance_announcement(self, settings: OrgComplianceAnnouncement, org_id: str = None)
166
+
167
+ # --- Compliance announcement (location) ---
168
+ def read_location_compliance_announcement(self, location_id: str, org_id: str = None) -> LocationComplianceAnnouncement
169
+ def update_location_compliance_announcement(self, location_id: str, settings: LocationComplianceAnnouncement, org_id: str = None)
170
+
171
+ # --- Regions ---
172
+ def get_call_recording_regions(self, org_id: str = None) -> list[CallRecordingRegion]
173
+
174
+ # --- Vendor users (org) ---
175
+ def list_org_users(self, standard_user_only: bool = None, org_id: str = None, **params) -> Generator[RecordingUser]
176
+
177
+ # --- Vendor management (org) ---
178
+ def get_org_vendors(self, org_id: str = None) -> CallRecordingVendors
179
+ def set_org_vendor(self, vendor_id: str, storage_region: str = None,
180
+ failure_behavior: FailureBehavior = None, org_id: str = None) -> str # returns jobId
181
+
182
+ # --- Vendor management (location) ---
183
+ def get_location_vendors(self, location_id: str, org_id: str = None) -> CallRecordingLocationVendors
184
+ def set_location_vendor(self, location_id: str, id: str = None,
185
+ org_default_enabled: bool = None, storage_region: str = None,
186
+ org_storage_region_enabled: bool = None,
187
+ failure_behavior: FailureBehavior = None,
188
+ org_failure_behavior_enabled: bool = None,
189
+ org_id: str = None) -> str # returns jobId
190
+
191
+ # --- Vendor users (location) ---
192
+ def list_location_users(self, location_id: str, standard_user_only: bool = None,
193
+ org_id: str = None, **params) -> Generator[RecordingUser]
194
+ ```
195
+
196
+ ### 1.4 Raw HTTP — Call Recording
197
+
198
+ ```python
199
+ BASE = "https://webexapis.com/v1"
200
+
201
+ # --- Org-level recording on/off ---
202
+ # GET — Get call recording settings
203
+ result = api.session.rest_get(f"{BASE}/telephony/config/callRecording")
204
+ # Response: {"organization": {"id": "...", "name": "..."}, "enabled": true,
205
+ # "vendorId": "...", "vendorName": "...", "termsOfServiceUrl": ""}
206
+
207
+ # PUT — Enable/disable call recording (Cisco partners only)
208
+ body = {"enabled": True}
209
+ api.session.rest_put(f"{BASE}/telephony/config/callRecording", json=body)
210
+
211
+ # --- Terms of service ---
212
+ # GET
213
+ result = api.session.rest_get(f"{BASE}/telephony/config/callRecording/vendors/{vendor_id}/termsOfService")
214
+ # PUT
215
+ body = {"termsOfServiceEnabled": True}
216
+ api.session.rest_put(f"{BASE}/telephony/config/callRecording/vendors/{vendor_id}/termsOfService", json=body)
217
+
218
+ # --- Compliance announcement (org) ---
219
+ # GET
220
+ result = api.session.rest_get(f"{BASE}/telephony/config/callRecording/complianceAnnouncement")
221
+ # Response: {"inboundPSTNCallsEnabled": false, "outboundPSTNCallsEnabled": false,
222
+ # "outboundPSTNCallsDelayEnabled": false, "delayInSeconds": 3}
223
+
224
+ # PUT
225
+ body = {
226
+ "inboundPSTNCallsEnabled": True,
227
+ "outboundPSTNCallsEnabled": True,
228
+ "outboundPSTNCallsDelayEnabled": True,
229
+ "delayInSeconds": 5
230
+ }
231
+ api.session.rest_put(f"{BASE}/telephony/config/callRecording/complianceAnnouncement", json=body)
232
+
233
+ # --- Compliance announcement (location) ---
234
+ # GET
235
+ result = api.session.rest_get(
236
+ f"{BASE}/telephony/config/locations/{loc_id}/callRecording/complianceAnnouncement"
237
+ )
238
+ # Response includes useOrgSettingsEnabled
239
+
240
+ # PUT
241
+ body = {
242
+ "useOrgSettingsEnabled": False,
243
+ "inboundPSTNCallsEnabled": True,
244
+ "outboundPSTNCallsEnabled": True,
245
+ "outboundPSTNCallsDelayEnabled": True,
246
+ "delayInSeconds": 3
247
+ }
248
+ api.session.rest_put(
249
+ f"{BASE}/telephony/config/locations/{loc_id}/callRecording/complianceAnnouncement",
250
+ json=body
251
+ )
252
+
253
+ # --- Regions ---
254
+ # GET
255
+ result = api.session.rest_get(f"{BASE}/telephony/config/callRecording/regions")
256
+ # Response: {"regions": [{"code": "US", "name": "United States", "defaultEnabled": true}]}
257
+
258
+ # --- Vendor users (org) ---
259
+ # GET (paginated)
260
+ result = api.session.rest_get(f"{BASE}/telephony/config/callRecording/vendorUsers",
261
+ params={"max": 1000, "standardUserOnly": True})
262
+ # Response: {"vendorUsers": [...]}
263
+
264
+ # --- Vendor management (org) ---
265
+ # GET — Get org vendors
266
+ result = api.session.rest_get(f"{BASE}/telephony/config/callRecording/vendors")
267
+
268
+ # PUT — Set org vendor (returns jobId)
269
+ body = {"vendorId": "<vendor_id>", "storageRegion": "US", "failureBehavior": "PROCEED_WITH_CALL_NO_ANNOUNCEMENT"}
270
+ result = api.session.rest_put(f"{BASE}/telephony/config/callRecording/vendor", json=body)
271
+ # Response 200: {"jobId": "..."} or 204 for immediate
272
+
273
+ # --- Vendor management (location) ---
274
+ # GET — Get location vendors
275
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callRecording/vendors")
276
+
277
+ # PUT — Set location vendor (returns jobId)
278
+ body = {
279
+ "id": "<vendor_id>",
280
+ "orgDefaultEnabled": False,
281
+ "storageRegion": "US",
282
+ "orgStorageRegionEnabled": False,
283
+ "failureBehavior": "PROCEED_WITH_CALL_NO_ANNOUNCEMENT",
284
+ "orgFailureBehaviorEnabled": False
285
+ }
286
+ result = api.session.rest_put(f"{BASE}/telephony/config/locations/{loc_id}/callRecording/vendor", json=body)
287
+
288
+ # --- Vendor users (location) ---
289
+ # GET (paginated)
290
+ result = api.session.rest_get(
291
+ f"{BASE}/telephony/config/locations/{loc_id}/callRecording/vendorUsers",
292
+ params={"max": 1000, "standardUserOnly": True}
293
+ )
294
+
295
+ # --- Call Recording Jobs ---
296
+ # GET — List jobs
297
+ result = api.session.rest_get(f"{BASE}/telephony/config/jobs/callRecording", params={"max": 100})
298
+
299
+ # GET — Job status
300
+ result = api.session.rest_get(f"{BASE}/telephony/config/jobs/callRecording/{job_id}")
301
+
302
+ # GET — Job errors
303
+ result = api.session.rest_get(f"{BASE}/telephony/config/jobs/callRecording/{job_id}/errors")
304
+ ```
305
+
306
+ ### 1.5 CLI Examples
307
+
308
+ ```bash
309
+ # Get org-level call recording settings
310
+ wxcli call-recording show
311
+
312
+ # Get org-level compliance announcement settings
313
+ wxcli call-recording show-compliance-announcement-call-recording
314
+
315
+ # Enable compliance announcement for inbound PSTN calls with 5-second delay
316
+ wxcli call-recording update-compliance-announcement-call-recording \
317
+ --inbound-pstn-calls-enabled --outbound-pstn-calls-enabled \
318
+ --outbound-pstn-calls-delay-enabled --delay-in-seconds 5
319
+
320
+ # Get location-level compliance announcement settings
321
+ wxcli call-recording show-compliance-announcement-call-recording-1 <location_id>
322
+
323
+ # Get org-level recording vendors
324
+ wxcli call-recording show-vendors
325
+
326
+ # Get location-level recording vendors
327
+ wxcli call-recording list-vendors <location_id>
328
+
329
+ # Set recording vendor for a location
330
+ wxcli call-recording update-vendor-call-recording <location_id> \
331
+ --id <vendor_id> --no-org-default-enabled \
332
+ --storage-region US --no-org-storage-region-enabled
333
+
334
+ # List call recording regions
335
+ wxcli call-recording list
336
+
337
+ # List vendor users at org level (paginated)
338
+ wxcli call-recording list-vendor-users-call-recording
339
+
340
+ # List vendor users at location level
341
+ wxcli call-recording list-vendor-users-call-recording-1 <location_id>
342
+
343
+ # List call recording jobs
344
+ wxcli call-recording list-call-recording
345
+
346
+ # Get status of a specific recording job
347
+ wxcli call-recording show-call-recording <job_id>
348
+
349
+ # Get errors for a recording job
350
+ wxcli call-recording list-errors <job_id>
351
+ ```
352
+
353
+ ### 1.6 Key Behaviors
354
+
355
+ - `set_org_vendor()` and `set_location_vendor()` return a **job ID** (string). Use the jobs API to check status if the change cannot be applied immediately (HTTP 200 with jobId vs. 204 for immediate).
356
+ - `update()` (enable/disable recording) is **Cisco partners only**.
357
+ - Storage region is **only applicable when vendor is Webex**; ignored for third-party vendors.
358
+ - `standard_user_only` parameter filters to Webex Calling standard license users only.
359
+
360
+ ---
361
+
362
+ ## 2. Caller Reputation
363
+
364
+ **SDK module:** `wxc_sdk.telephony.caller_reputation`
365
+ **API class:** `CallerReputationProviderApi` (base: `telephony/config/serviceSettings/callerReputationProvider`)
366
+
367
+ Integrates with external calling reputation providers for spam/fraud call scoring.
368
+
369
+ ### 2.1 Data Models
370
+
371
+ #### `ReputationProviderSettings`
372
+
373
+ | Field | Type | Notes |
374
+ |-------|------|-------|
375
+ | `name` | `str` | Provider name |
376
+ | `id` | `str` | Provider ID |
377
+ | `client_id` | `str` | OAuth client ID for integration |
378
+ | `client_secret` | `str` | Write-only; not returned on read |
379
+ | `enabled` | `bool` | Service enabled/disabled |
380
+ | `call_block_score_threshold` | `str` | Score at which calls are blocked |
381
+ | `call_allow_score_threshold` | `str` | Score at which calls are allowed |
382
+
383
+ #### `ReputationProviderState` (Enum)
384
+
385
+ | Value | Meaning |
386
+ |-------|---------|
387
+ | `NOT_CONNECTED` | Not connected |
388
+ | `CONNECTING` | Connection in progress |
389
+ | `CONNECTED` | Connected |
390
+ | `ACTIVE` | Active and operational |
391
+ | `EXPIRED` | Session/token expired |
392
+ | `AUTH_FAILED` | Authentication failed |
393
+ | `PROVIDER_DISABLED` | Provider disabled |
394
+
395
+ #### `ReputationProviderStatus`
396
+
397
+ | Field | Type |
398
+ |-------|------|
399
+ | `id` | `str` |
400
+ | `status` | `ReputationProviderState` |
401
+
402
+ #### `ReputationProviderRegion`
403
+
404
+ | Field | Type | Notes |
405
+ |-------|------|-------|
406
+ | `id` | `str` | |
407
+ | `name` | `str` | |
408
+ | `type` | `str` | e.g., primary, secondary |
409
+ | `visible` | `bool` | |
410
+ | `environment_type` | `str` | e.g., production, staging |
411
+
412
+ #### `CallerReputationProviderProvider`
413
+
414
+ | Field | Type |
415
+ |-------|------|
416
+ | `id` | `str` |
417
+ | `enabled` | `bool` |
418
+ | `name` | `str` |
419
+ | `regions` | `list[ReputationProviderRegion]` |
420
+
421
+ ### 2.2 API Methods
422
+
423
+ ```python
424
+ class CallerReputationProviderApi:
425
+
426
+ def get(self, organization_id: str = None) -> ReputationProviderSettings
427
+ def update(self, settings: ReputationProviderSettings, organization_id: str = None)
428
+ def unlock(self, rep_id: str, organization_id: str = None)
429
+ def providers(self, organization_id: str = None) -> list[CallerReputationProviderProvider]
430
+ def status(self, organization_id: str = None) -> ReputationProviderStatus
431
+ ```
432
+
433
+ ### 2.3 Raw HTTP — Caller Reputation
434
+
435
+ ```python
436
+ BASE = "https://webexapis.com/v1"
437
+
438
+ # GET — Get caller reputation provider settings
439
+ result = api.session.rest_get(
440
+ f"{BASE}/telephony/config/serviceSettings/callerReputationProvider")
441
+ # Response: {"name": "...", "id": "...", "clientId": "...", "enabled": true,
442
+ # "callBlockScoreThreshold": "80", "callAllowScoreThreshold": "20"}
443
+ # Note: clientSecret is NEVER returned by GET
444
+
445
+ # PUT — Update caller reputation provider settings
446
+ body = {
447
+ "enabled": True,
448
+ "id": "<provider_id>",
449
+ "name": "Provider Name",
450
+ "clientId": "<oauth_client_id>",
451
+ "clientSecret": "<oauth_client_secret>",
452
+ "callBlockScoreThreshold": "80",
453
+ "callAllowScoreThreshold": "20"
454
+ }
455
+ api.session.rest_put(
456
+ f"{BASE}/telephony/config/serviceSettings/callerReputationProvider", json=body)
457
+
458
+ # GET — Get provider status
459
+ result = api.session.rest_get(
460
+ f"{BASE}/telephony/config/serviceSettings/callerReputationProvider/status")
461
+ # Response: {"id": "...", "status": "CONNECTED"}
462
+
463
+ # GET — List available providers
464
+ result = api.session.rest_get(
465
+ f"{BASE}/telephony/config/serviceSettings/callerReputationProvider/providers")
466
+ # Response: {"providers": [{"id": "...", "enabled": true, "name": "...",
467
+ # "regions": [{"id": "...", "name": "...", "type": "primary", "visible": true}]}]}
468
+
469
+ # POST — Unlock a locked provider
470
+ body = {"id": "<provider_id>"}
471
+ api.session.rest_post(
472
+ f"{BASE}/telephony/config/serviceSettings/callerReputationProvider/actions/unlock/invoke",
473
+ json=body)
474
+ ```
475
+
476
+ **Gotcha:** This API uses `organizationId` as the query parameter (not `orgId` like most other telephony APIs).
477
+
478
+ **Gotcha:** Score thresholds are strings, not integers.
479
+ <!-- Updated by playbook session 2026-03-18 -->
480
+
481
+ ### 2.4 CLI Examples
482
+
483
+ ```bash
484
+ # Get caller reputation provider settings
485
+ wxcli caller-reputation show
486
+
487
+ # Get caller reputation provider status
488
+ wxcli caller-reputation show-status
489
+
490
+ # List available caller reputation providers
491
+ wxcli caller-reputation list
492
+
493
+ # Update caller reputation provider settings
494
+ wxcli caller-reputation update \
495
+ --enabled --id <provider_id> --name "Provider Name" \
496
+ --client-id <oauth_client_id> --client-secret <oauth_client_secret> \
497
+ --call-block-score-threshold "80" --call-allow-score-threshold "20"
498
+
499
+ # Unlock a locked caller reputation provider
500
+ wxcli caller-reputation unlock-caller-reputation
501
+
502
+ # Query for a specific org (partner scenario)
503
+ wxcli caller-reputation show --organization-id <org_id>
504
+ ```
505
+
506
+ ### 2.5 Key Behaviors
507
+
508
+ - **`client_secret` is write-only** -- it is never returned by `get()`. Only usable in `update()`.
509
+ - The `unlock()` method invokes `actions/unlock/invoke` -- use this when the provider is in a locked state (e.g., after auth failure). <!-- UNVERIFIABLE: exact conditions that trigger a locked state are not documented in SDK or OpenAPI spec; would need live API testing -->
510
+ - Note the parameter is `organization_id` (not `org_id`) in this API, differing from most other telephony APIs.
511
+ - Score thresholds are strings, not integers. The OpenAPI spec examples show decimal numeric strings (e.g., `"0.7"`, `"0.3"`), not integer strings.
512
+
513
+ ---
514
+
515
+ ## 3. Conference Controls
516
+
517
+ **SDK module:** `wxc_sdk.telephony.conference`
518
+ **API class:** `ConferenceControlsApi` (base: `telephony/conference`)
519
+
520
+ Runtime conference call management (not configuration). This is a **call-control** API, not an admin config API.
521
+
522
+ ### 3.1 Scopes
523
+
524
+ | Operation | Scope |
525
+ |-----------|-------|
526
+ | GET | `spark:calls_read` |
527
+ | All others | `spark:calls_write` |
528
+
529
+ ### 3.2 Data Models
530
+
531
+ #### `ConferenceState` (Enum)
532
+
533
+ | Value | Meaning |
534
+ |-------|---------|
535
+ | `connected` | Host is active participant |
536
+ | `held` | Host has held the conference |
537
+ | `disconnected` | Conference released |
538
+
539
+ #### `ConferenceTypeEnum` (Enum)
540
+
541
+ | Value | Usage |
542
+ |-------|-------|
543
+ | `bargeIn` | Barge-in conference |
544
+ | `silentMonitoring` | Silent monitoring |
545
+ | `coaching` | Coaching session |
546
+
547
+ #### `ConferenceParticipant`
548
+
549
+ | Field | Type | Notes |
550
+ |-------|------|-------|
551
+ | `call_id` | `str` | Call identifier for this participant |
552
+ | `muted` | `bool` | |
553
+ | `deafened` | `bool` | Media not transmitted to participant |
554
+
555
+ #### `ConferenceDetails`
556
+
557
+ | Field | Type | Notes |
558
+ |-------|------|-------|
559
+ | `state` | `ConferenceState` | |
560
+ | `appearance` | `int` | Appearance index (if assigned) |
561
+ | `created` | `datetime` | ISO 8601 start time |
562
+ | `muted` | `bool` | Host muted |
563
+ | `type` | `ConferenceTypeEnum` | Only for non-standard conferences |
564
+ | `participants` | `list[ConferenceParticipant]` | |
565
+
566
+ ### 3.3 API Methods
567
+
568
+ ```python
569
+ class ConferenceControlsApi:
570
+
571
+ def start_conference(self, call_ids: list[str], line_owner_id: str = None)
572
+ def get_conference_details(self, line_owner_id: str = None) -> ConferenceDetails
573
+ def release_conference(self, line_owner_id: str = None)
574
+ def add_participant(self, call_id: str, line_owner_id: str = None)
575
+ def hold(self, line_owner_id: str = None)
576
+ def resume(self, line_owner_id: str = None)
577
+ def mute(self, call_id: str = None) # host if call_id omitted
578
+ def unmute(self, call_id: str = None) # host if call_id omitted
579
+ def deafen_participant(self, call_id: str)
580
+ def undeafen_participant(self, call_id: str)
581
+ ```
582
+
583
+ ### 3.4 CLI Examples
584
+
585
+ ```bash
586
+ # Get current conference details (requires user-level OAuth token)
587
+ wxcli conference list
588
+
589
+ # Get conference details for a specific line owner
590
+ wxcli conference list --line-owner-id <user_id>
591
+
592
+ # Start a conference by merging two active calls
593
+ wxcli conference create --json-body '{"callIds": ["call_id_1", "call_id_2"]}'
594
+
595
+ # Add a participant to an active conference
596
+ wxcli conference create-add-participant --json-body '{"callId": "call_id_3"}'
597
+
598
+ # Hold / resume the conference
599
+ wxcli conference create-hold
600
+ wxcli conference create-resume
601
+
602
+ # Mute the host
603
+ wxcli conference create-mute
604
+
605
+ # Mute a specific participant
606
+ wxcli conference create-mute --json-body '{"callId": "participant_call_id"}'
607
+
608
+ # Unmute
609
+ wxcli conference create-unmute
610
+
611
+ # Deafen / undeafen a participant
612
+ wxcli conference create-deafen --json-body '{"callId": "participant_call_id"}'
613
+ wxcli conference create-undeafen --json-body '{"callId": "participant_call_id"}'
614
+
615
+ # Release (end) the conference
616
+ wxcli conference delete
617
+ ```
618
+
619
+ > **Note:** Conference controls require a **user-level OAuth token** (`spark:calls_write` scope). Admin tokens will not work. See Known Issues in CLAUDE.md.
620
+
621
+ ### 3.5 Key Behaviors
622
+
623
+ - **`start_conference()`** requires a minimum of **two call IDs**. Each must identify an existing call between the invoking user and a participant.
624
+ - **`get_conference_details()`** returns an **empty JSON object** if no conference exists.
625
+ - **Mute vs. Deafen**: Muting stops a participant's audio from being transmitted to the conference. Deafening stops the conference audio from being transmitted to the participant. They are independent.
626
+ - **`mute()` / `unmute()`** operate on the host when `call_id` is omitted, or on a specific participant when provided.
627
+ - **`line_owner_id`** is used when invoking the API on behalf of a secondary line owner (user, workspace, or virtual line).
628
+ - For three-way calls (3WC), the Transfer API can be used instead of `release_conference()` to keep participants connected while the host drops.
629
+
630
+ ---
631
+
632
+ ## 4. Supervisors
633
+
634
+ > For the full Customer Assist setup workflow including supervisors, use the **customer-assist** skill.
635
+
636
+ **SDK module:** `wxc_sdk.telephony.supervisor`
637
+ **API class:** `SupervisorApi` (base: `telephony/config/supervisors`)
638
+
639
+ Supervisors manage call queue agents. They can silently monitor, coach, barge in, or take over calls their agents are handling.
640
+
641
+ ### 4.1 Scopes
642
+
643
+ | Operation | Scope |
644
+ |-----------|-------|
645
+ | Read | `spark-admin:telephony_config_read` |
646
+ | Write | `spark-admin:telephony_config_write` |
647
+
648
+ ### 4.2 Data Models
649
+
650
+ #### `AgentOrSupervisor`
651
+ Used for both supervisor and agent listings.
652
+
653
+ | Field | Type | Notes |
654
+ |-------|------|-------|
655
+ | `id` | `str` | |
656
+ | `first_name` | `str` | |
657
+ | `last_name` | `str` | |
658
+ | `display_name` | `str` | |
659
+ | `phone_number` | `str` | |
660
+ | `extension` | `str` | |
661
+ | `routing_prefix` | `str` | Location routing prefix |
662
+ | `esn` | `str` | Routing prefix + extension |
663
+ | `type` | `UserType` | Person, workspace, or virtual line |
664
+ | `has_cx_essentials` | `bool` | Has Customer Assist (formerly CX Essentials) license |
665
+ | `agent_count` | `int` | Agents managed (supervisors only) |
666
+
667
+ #### `IdAndAction`
668
+
669
+ | Field | Type | Notes |
670
+ |-------|------|-------|
671
+ | `id` | `str` | Person, workspace, or virtual line ID |
672
+ | `action` | `PatternAction` | `ADD` or `DELETE` |
673
+
674
+ #### `SupervisorAgentStatus`
675
+
676
+ | Field | Type | Notes |
677
+ |-------|------|-------|
678
+ | `id` | `str` | |
679
+ | `status` | `str` | Status result |
680
+ | `message` | `str` | Detail message |
681
+ | `type` | `UserType` | SDK-only field; absent from OpenAPI spec `ListSupervisorAgentStatusObject`. SDK marks as `# TODO: undocumented, issue 202` |
682
+
683
+ ### 4.3 API Methods
684
+
685
+ ```python
686
+ class SupervisorApi:
687
+
688
+ # --- List / search ---
689
+ def list(self, name: str = None, phone_number: str = None, order: str = None,
690
+ has_cx_essentials: bool = None, org_id: str = None,
691
+ **params) -> Generator[AgentOrSupervisor]
692
+
693
+ def available_supervisors(self, name: str = None, phone_number: str = None, order: str = None,
694
+ has_cx_essentials: bool = None, org_id: str = None,
695
+ **params) -> Generator[AgentOrSupervisor]
696
+
697
+ def available_agents(self, name: str = None, phone_number: str = None, order: str = None,
698
+ has_cx_essentials: bool = None, org_id: str = None,
699
+ **params) -> Generator[AgentOrSupervisor]
700
+
701
+ # --- CRUD ---
702
+ def create(self, id: str, agents: list[str], has_cx_essentials: bool = None, org_id: str = None)
703
+ def delete(self, supervisor_id: str, org_id: str = None)
704
+ def delete_bulk(self, supervisors_ids: list[str], delete_all: bool = None, org_id: str = None)
705
+
706
+ # --- Details and agent assignment ---
707
+ def details(self, supervisor_id: str, name: str = None, phone_number: str = None,
708
+ order: str = None, has_cx_essentials: bool = None,
709
+ org_id: str = None, **additional_params) -> Generator[AgentOrSupervisor]
710
+
711
+ def assign_unassign_agents(self, supervisor_id: str, agents: list[IdAndAction],
712
+ has_cx_essentials: bool = None,
713
+ org_id: str = None) -> Optional[list[SupervisorAgentStatus]]
714
+ ```
715
+
716
+ ### 4.4 CLI Examples
717
+
718
+ Supervisor commands are under the `wxcli call-queue` group. **For Customer Assist supervisors, always include `--has-cx-essentials true`.**
719
+
720
+ ```bash
721
+ # List all Customer Assist supervisors
722
+ wxcli call-queue list-supervisors --has-cx-essentials true
723
+
724
+ # List available supervisors (not yet assigned)
725
+ wxcli call-queue list-available-supervisors --has-cx-essentials true
726
+
727
+ # List available agents for supervisor assignment
728
+ wxcli call-queue list-available-agents-supervisors --has-cx-essentials true
729
+
730
+ # Create a supervisor with agents
731
+ wxcli call-queue create-supervisors --has-cx-essentials true \
732
+ --id SUPERVISOR_PERSON_ID --json-body '{"agents": [{"id": "AGENT_ID"}]}'
733
+
734
+ # Show supervisor's assigned agents
735
+ wxcli call-queue show-supervisors SUPERVISOR_ID --has-cx-essentials true -o json
736
+
737
+ # Update supervisor's agents (incremental add/remove)
738
+ wxcli call-queue update-supervisors SUPERVISOR_ID --has-cx-essentials true \
739
+ --json-body '{"agents": [{"id": "AGENT_ID", "action": "ADD"}]}'
740
+
741
+ # Delete a specific supervisor
742
+ wxcli call-queue delete-supervisors-config-1 SUPERVISOR_ID --has-cx-essentials true --force
743
+
744
+ # Delete supervisors in bulk
745
+ wxcli call-queue delete-supervisors-config --has-cx-essentials true --force
746
+
747
+ # RECOMMENDED: Remove supervisor by removing all agents (more reliable)
748
+ wxcli call-queue update-supervisors SUPERVISOR_ID --has-cx-essentials true \
749
+ --json-body '{"agents": [{"id": "AGENT_ID", "action": "DELETE"}]}'
750
+ # When last agent is removed, supervisor is auto-deleted
751
+ ```
752
+
753
+ > **WARNING:** `delete-supervisors-config --force` without specifying IDs may remove **all supervisors in the org**. Always confirm scope before executing.
754
+
755
+ > **GOTCHA:** `delete-supervisors-config-1` returns 204 but the Customer Assist supervisor may persist. The reliable method is to remove all agents via `update-supervisors` with `action: DELETE` — when the last agent is removed, the supervisor is automatically deleted.
756
+
757
+ ### 4.5 Key Behaviors
758
+
759
+ - **A supervisor must have at least one agent** when created via `create()`.
760
+ - **`create()` takes agent IDs as a flat `list[str]`**, not `IdAndAction` objects. The SDK wraps them as `[{'id': agent_id}]` internally.
761
+ - **`assign_unassign_agents()`** uses `IdAndAction` with `PatternAction.ADD` or `PatternAction.DELETE` to add/remove agents in a single call. Returns `None` if all succeed, or a list of `SupervisorAgentStatus` with per-agent error details.
762
+ - **`delete_bulk()`** has a `delete_all` parameter. When set to `True`, the `supervisors_ids` array is ignored and **all supervisors in the org are removed**. Use with extreme caution.
763
+ - **Customer Assist vs. CX Basic**: The `has_cx_essentials` parameter gates which license tier you are querying/modifying. When `True`, returns/operates on Customer Assist supervisors only. When omitted or `False`, operates on CX Basic.
764
+ - **`details()`** returns a generator of the supervisor's assigned **agents** (not the supervisor's own details). The item key is `agents`.
765
+
766
+ ---
767
+
768
+ ## 5. Guest Calling (Click-to-Call)
769
+
770
+ **SDK module:** `wxc_sdk.telephony.guest_calling`
771
+ **API class:** `GuestCallingApi` (base: `telephony/config/guestCalling`)
772
+
773
+ Click-to-call allows external (guest) callers to reach internal destinations. Org-level setting.
774
+
775
+ ### 5.1 Scopes
776
+
777
+ | Operation | Scope |
778
+ |-----------|-------|
779
+ | Read | `spark-admin:telephony_config_read` |
780
+ | Write | `spark-admin:telephony_config_write` |
781
+
782
+ ### 5.2 Data Models
783
+
784
+ #### `GuestCallingSettings`
785
+
786
+ | Field | Type | Notes |
787
+ |-------|------|-------|
788
+ | `enabled` | `bool` | Click-to-call enabled |
789
+ | `privacy_enabled` | `bool` | Privacy mode |
790
+ | `video_enabled` | `bool` | SDK-only field with no docstring; absent from all OpenAPI specs. May be undocumented or deprecated |
791
+
792
+ #### `DestinationMember`
793
+
794
+ | Field | Type | Notes |
795
+ |-------|------|-------|
796
+ | `id` | `str` | |
797
+ | `first_name` | `str` | |
798
+ | `last_name` | `str` | |
799
+ | `phone_number` | `str` | |
800
+ | `extension` | `str` | |
801
+ | `routing_prefix` | `str` | |
802
+ | `esn` | `str` | |
803
+ | `type` | `OwnerType` | |
804
+ | `location` | `IdAndName` | |
805
+
806
+ ### 5.3 API Methods
807
+
808
+ ```python
809
+ class GuestCallingApi:
810
+
811
+ def read(self, org_id: str = None) -> GuestCallingSettings
812
+ def update(self, enabled: bool, privacy_enabled: bool,
813
+ destination_members: list[str], org_id: str = None)
814
+ def members(self, member_name: str = None, phone_number: str = None,
815
+ extension: str = None, org_id: str = None,
816
+ **params) -> Generator[DestinationMember]
817
+ def available_members(self, member_name: str = None, phone_number: str = None,
818
+ extension: str = None, org_id: str = None,
819
+ **params) -> Generator[DestinationMember]
820
+ ```
821
+
822
+ ### 5.4 CLI Examples
823
+
824
+ There is no dedicated `wxcli` guest-calling (click-to-call) command group. Use the SDK methods or Raw HTTP calls. The `guest-management` group covers a different feature (guest user creation), not click-to-call destinations.
825
+
826
+ ### 5.5 Key Behaviors
827
+
828
+ - **Supported destination types**: Auto Attendant, Call Queue, Hunt Group, and Virtual Line. Not individual users.
829
+ - **`update()` takes destination member IDs** as a flat `list[str]`, not full `DestinationMember` objects.
830
+ - **`members()`** returns currently assigned click-to-call destinations.
831
+ - **`available_members()`** returns destinations that can be added but are not yet assigned.
832
+ - All search parameters (`member_name`, `phone_number`, `extension`) use **contains** matching.
833
+
834
+ ---
835
+
836
+ ## 6. Operating Modes
837
+
838
+ **SDK module:** `wxc_sdk.telephony.operating_modes`
839
+ **API class:** `OperatingModesApi` (base: `telephony/config`)
840
+
841
+ Operating modes define time-based call routing rules (business hours, after hours, holidays, etc.) used by Auto Attendants, Call Queues, and Hunt Groups via mode-based forwarding.
842
+
843
+ ### 6.1 Scopes
844
+
845
+ | Operation | Scope |
846
+ |-----------|-------|
847
+ | Read | `spark-admin:telephony_config_read` |
848
+ | Write | `spark-admin:telephony_config_write` |
849
+
850
+ ### 6.2 Data Models
851
+
852
+ #### `OperatingModeSchedule` (Enum)
853
+
854
+ | Value | Meaning |
855
+ |-------|---------|
856
+ | `SAME_HOURS_DAILY` | Same schedule Mon-Fri and Sat-Sun |
857
+ | `DIFFERENT_HOURS_DAILY` | Per-day schedule |
858
+ | `HOLIDAY` | Holiday-based with recurrence |
859
+ | `NONE` | No schedule defined |
860
+
861
+ #### `DaySchedule`
862
+
863
+ | Field | Type | Notes |
864
+ |-------|------|-------|
865
+ | `enabled` | `bool` | Schedule active for this day/group |
866
+ | `all_day_enabled` | `bool` | Active entire day |
867
+ | `start_time` | `time` | HH:MM format |
868
+ | `end_time` | `time` | HH:MM format |
869
+
870
+ #### `SameHoursDaily`
871
+
872
+ | Field | Type |
873
+ |-------|------|
874
+ | `monday_to_friday` | `DaySchedule` |
875
+ | `saturday_to_sunday` | `DaySchedule` |
876
+
877
+ #### `DifferentHoursDaily`
878
+ Individual `DaySchedule` fields for: `sunday`, `monday`, `tuesday`, `wednesday`, `thursday`, `friday`, `saturday`.
879
+
880
+ #### `OperatingModeHoliday`
881
+
882
+ | Field | Type | Notes |
883
+ |-------|------|-------|
884
+ | `id` | `str` | |
885
+ | `name` | `str` | |
886
+ | `all_day_enabled` | `bool` | |
887
+ | `start_date` | `date` | YYYY-MM-DD format |
888
+ | `end_date` | `date` | |
889
+ | `start_time` | `time` | Required if `all_day_enabled` is false |
890
+ | `end_time` | `time` | Required if `all_day_enabled` is false |
891
+ | `recurrence` | `OperatingModeRecurrence` | |
892
+
893
+ #### `OperatingModeRecurrence`
894
+
895
+ | Field | Type |
896
+ |-------|------|
897
+ | `recur_yearly_by_date` | `OperatingModeRecurYearlyByDate` |
898
+ | `recur_yearly_by_day` | `OperatingModeRecurYearlyByDay` |
899
+
900
+ Two recurrence patterns: by date (day of month + month) or by day (day + week + month, e.g., "first Monday of September").
901
+
902
+ #### `OperatingMode`
903
+
904
+ | Field | Type | Notes |
905
+ |-------|------|-------|
906
+ | `id` | `str` | |
907
+ | `name` | `str` | |
908
+ | `type` | `OperatingModeSchedule` | |
909
+ | `level` | `ScheduleLevel` | Org or location |
910
+ | `location` | `IdAndName` | Required if level is LOCATION |
911
+ | `same_hours_daily` | `SameHoursDaily` | Present if type is `SAME_HOURS_DAILY` |
912
+ | `different_hours_daily` | `DifferentHoursDaily` | Present if type is `DIFFERENT_HOURS_DAILY` |
913
+ | `holidays` | `list[OperatingModeHoliday]` | Present if type is `HOLIDAY` |
914
+ | `call_forwarding` | `CallForwardingCommon` | Forwarding settings for this mode |
915
+
916
+ ### 6.3 API Methods
917
+
918
+ ```python
919
+ class OperatingModesApi:
920
+
921
+ # --- CRUD ---
922
+ def list(self, limit_to_location_id: str = None, name: str = None,
923
+ limit_to_org_level_enabled: bool = None, order: str = None,
924
+ org_id: str = None, **params) -> Generator[OperatingMode]
925
+
926
+ def details(self, mode_id: str, org_id: str = None) -> OperatingMode
927
+ def create(self, settings: OperatingMode, org_id: str = None) -> str # returns mode ID
928
+ def update(self, mode_id: str, settings: OperatingMode, org_id: str = None)
929
+ def delete(self, mode_id: str, org_id: str = None)
930
+
931
+ # --- Holiday management ---
932
+ def holiday_details(self, mode_id: str, holiday_id: str, org_id: str = None) -> OperatingModeHoliday
933
+ def holiday_create(self, mode_id: str, settings: OperatingModeHoliday, org_id: str = None) -> str # returns holiday ID
934
+ # Verified via CLI implementation 2026-03-17: holiday_create requires start_date and end_date (not just date), plus all_day_enabled. Only HOLIDAY type operating modes accept holiday events.
935
+ def holiday_update(self, mode_id: str, holiday_id: str, settings: OperatingModeHoliday, org_id: str = None)
936
+ def holiday_delete(self, mode_id: str, holiday_id: str = None, org_id: str = None)
937
+
938
+ # --- Availability queries ---
939
+ def available_operating_modes(self, location_id: str, org_id: str = None) -> list[IdAndName]
940
+ def call_forward_available_phone_numbers(self, location_id: str, phone_number: list[str] = None,
941
+ owner_name: str = None, extension: str = None,
942
+ org_id: str = None, **params) -> Generator[AvailableNumber]
943
+ ```
944
+
945
+ ### 6.4 Raw HTTP — Operating Modes
946
+
947
+ ```python
948
+ BASE = "https://webexapis.com/v1"
949
+
950
+ # --- Operating Mode CRUD ---
951
+
952
+ # GET — List operating modes (paginated)
953
+ result = api.session.rest_get(f"{BASE}/telephony/config/operatingModes",
954
+ params={"max": 1000, "limitToLocationId": loc_id})
955
+ # Response: {"operatingModes": [{"id": "...", "name": "...", "type": "SAME_HOURS_DAILY",
956
+ # "level": "ORGANIZATION"}]}
957
+ # Optional params: name, limitToOrgLevelEnabled, order (asc/desc by name)
958
+
959
+ # GET — Get operating mode details
960
+ result = api.session.rest_get(f"{BASE}/telephony/config/operatingModes/{mode_id}")
961
+ # Response includes full schedule data (sameHoursDaily, differentHoursDaily, or holidays)
962
+ # plus callForwarding settings
963
+
964
+ # POST — Create an operating mode
965
+ body = {
966
+ "name": "Business Hours",
967
+ "type": "SAME_HOURS_DAILY",
968
+ "level": "ORGANIZATION",
969
+ "sameHoursDaily": {
970
+ "mondayToFriday": {
971
+ "enabled": True,
972
+ "allDayEnabled": False,
973
+ "startTime": "09:00",
974
+ "endTime": "17:00"
975
+ },
976
+ "saturdayToSunday": {
977
+ "enabled": False
978
+ }
979
+ }
980
+ }
981
+ result = api.session.rest_post(f"{BASE}/telephony/config/operatingModes/", json=body)
982
+ # Returns: {"id": "<mode_id>"}
983
+
984
+ # For location-scoped:
985
+ body = {
986
+ "name": "Branch Office Hours",
987
+ "type": "DIFFERENT_HOURS_DAILY",
988
+ "level": "LOCATION",
989
+ "locationId": "<loc_id>",
990
+ "differentHoursDaily": {
991
+ "monday": {"enabled": True, "startTime": "08:00", "endTime": "18:00"},
992
+ "tuesday": {"enabled": True, "startTime": "08:00", "endTime": "18:00"},
993
+ "wednesday": {"enabled": True, "startTime": "08:00", "endTime": "18:00"},
994
+ "thursday": {"enabled": True, "startTime": "08:00", "endTime": "18:00"},
995
+ "friday": {"enabled": True, "startTime": "08:00", "endTime": "16:00"},
996
+ "saturday": {"enabled": False},
997
+ "sunday": {"enabled": False}
998
+ }
999
+ }
1000
+ result = api.session.rest_post(f"{BASE}/telephony/config/operatingModes/", json=body)
1001
+
1002
+ # PUT — Update an operating mode
1003
+ body = {"name": "Updated Business Hours"}
1004
+ api.session.rest_put(f"{BASE}/telephony/config/operatingModes/{mode_id}", json=body)
1005
+
1006
+ # DELETE — Delete an operating mode
1007
+ api.session.rest_delete(f"{BASE}/telephony/config/operatingModes/{mode_id}")
1008
+
1009
+ # --- Holiday management (only for HOLIDAY type operating modes) ---
1010
+
1011
+ # GET — Get holiday details
1012
+ result = api.session.rest_get(
1013
+ f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays/{holiday_id}")
1014
+
1015
+ # POST — Create a holiday
1016
+ body = {
1017
+ "name": "Independence Day",
1018
+ "allDayEnabled": True,
1019
+ "startDate": "2026-07-04",
1020
+ "endDate": "2026-07-04"
1021
+ }
1022
+ result = api.session.rest_post(
1023
+ f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays", json=body)
1024
+ # Returns: {"id": "<holiday_id>"}
1025
+
1026
+ # POST — Create holiday with yearly recurrence
1027
+ body = {
1028
+ "name": "Christmas",
1029
+ "allDayEnabled": True,
1030
+ "startDate": "2026-12-25",
1031
+ "endDate": "2026-12-25",
1032
+ "recurrence": {
1033
+ "recurYearlyByDate": {"dayOfMonth": 25, "month": "DECEMBER"}
1034
+ }
1035
+ }
1036
+ result = api.session.rest_post(
1037
+ f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays", json=body)
1038
+
1039
+ # PUT — Update a holiday
1040
+ body = {"name": "Christmas Day", "allDayEnabled": True,
1041
+ "startDate": "2026-12-25", "endDate": "2026-12-25"}
1042
+ api.session.rest_put(
1043
+ f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays/{holiday_id}", json=body)
1044
+
1045
+ # DELETE — Delete a holiday
1046
+ api.session.rest_delete(
1047
+ f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays/{holiday_id}")
1048
+
1049
+ # --- Availability queries ---
1050
+
1051
+ # GET — Available operating modes for a location (up to 200: location + org combined)
1052
+ result = api.session.rest_get(
1053
+ f"{BASE}/telephony/config/locations/{loc_id}/operatingModes/availableOperatingModes")
1054
+ # Response: {"availableOperatingModes": [{"id": "...", "name": "..."}]}
1055
+
1056
+ # GET — Available phone numbers for call forwarding in operating modes
1057
+ result = api.session.rest_get(
1058
+ f"{BASE}/telephony/config/locations/{loc_id}/operatingModes/callForwarding/availableNumbers",
1059
+ params={"max": 1000})
1060
+ # Response: {"availableNumbers": [{"phoneNumber": "+1...", "ownerName": "...", "extension": "..."}]}
1061
+ ```
1062
+
1063
+ **Gotcha:** Create requires `level` set to `ORGANIZATION` or `LOCATION`. For `LOCATION`, you must also include `locationId` in the body. The `type` field must use the enum value directly (e.g., `SAME_HOURS_DAILY`, `DIFFERENT_HOURS_DAILY`, `HOLIDAY`).
1064
+
1065
+ **Gotcha:** For `SAME_HOURS_DAILY` type, you must provide actual schedule data in `sameHoursDaily` -- the API rejects a create with no schedule data.
1066
+
1067
+ **Gotcha:** Holidays can only be added to operating modes with `type: HOLIDAY`. The `holiday_create` endpoint requires `startDate`, `endDate`, and `allDayEnabled` at minimum.
1068
+
1069
+ ### 6.5 CLI Examples
1070
+
1071
+ ```bash
1072
+ # List all operating modes
1073
+ wxcli operating-modes list
1074
+
1075
+ # List operating modes for a specific location
1076
+ wxcli operating-modes list --limit-to-location-id <location_id>
1077
+
1078
+ # List org-level operating modes only
1079
+ wxcli operating-modes list --limit-to-org-level-enabled true
1080
+
1081
+ # Search operating modes by name
1082
+ wxcli operating-modes list --name "Business Hours"
1083
+
1084
+ # Get details for an operating mode
1085
+ wxcli operating-modes show <mode_id>
1086
+
1087
+ # Create an operating mode (requires --json-body for schedule data)
1088
+ wxcli operating-modes create --name "Business Hours" --json-body '{
1089
+ "type": "SAME_HOURS_DAILY",
1090
+ "level": "ORGANIZATION",
1091
+ "sameHoursDaily": {
1092
+ "mondayToFriday": {"enabled": true, "startTime": "09:00", "endTime": "17:00"},
1093
+ "saturdayToSunday": {"enabled": false}
1094
+ }
1095
+ }'
1096
+
1097
+ # Create a location-scoped operating mode
1098
+ wxcli operating-modes create --name "Branch Hours" \
1099
+ --location-id <location_id> --json-body '{
1100
+ "type": "DIFFERENT_HOURS_DAILY",
1101
+ "level": "LOCATION",
1102
+ "differentHoursDaily": {
1103
+ "monday": {"enabled": true, "startTime": "08:00", "endTime": "18:00"},
1104
+ "tuesday": {"enabled": true, "startTime": "08:00", "endTime": "18:00"},
1105
+ "wednesday": {"enabled": true, "startTime": "08:00", "endTime": "18:00"},
1106
+ "thursday": {"enabled": true, "startTime": "08:00", "endTime": "18:00"},
1107
+ "friday": {"enabled": true, "startTime": "08:00", "endTime": "16:00"},
1108
+ "saturday": {"enabled": false},
1109
+ "sunday": {"enabled": false}
1110
+ }
1111
+ }'
1112
+
1113
+ # Update an operating mode
1114
+ wxcli operating-modes update <mode_id> --json-body '{"name": "Updated Business Hours"}'
1115
+
1116
+ # Delete an operating mode
1117
+ wxcli operating-modes delete <mode_id>
1118
+
1119
+ # Create a holiday on a HOLIDAY-type operating mode
1120
+ wxcli operating-modes create-holidays <mode_id> \
1121
+ --name "Independence Day" --all-day-enabled \
1122
+ --start-date "2026-07-04" --end-date "2026-07-04"
1123
+
1124
+ # Get holiday details
1125
+ wxcli operating-modes show-holidays <mode_id> <holiday_id>
1126
+
1127
+ # Delete a holiday
1128
+ wxcli operating-modes delete-holidays <mode_id> <holiday_id>
1129
+
1130
+ # List available operating modes for a location
1131
+ wxcli operating-modes list-available-operating-modes <location_id>
1132
+
1133
+ # List available phone numbers for call forwarding in operating modes
1134
+ wxcli operating-modes list-available-numbers <location_id>
1135
+ ```
1136
+
1137
+ ### 6.6 Key Behaviors
1138
+
1139
+ - **Max 100 operating modes per location** and 100 per org. `available_operating_modes()` returns up to 200 (location + org combined).
1140
+ - **Max 150 holidays per operating mode.**
1141
+ - The `create()` method requires at least `name`, `type`, and `level` on the `OperatingMode` object. If `level` is LOCATION, `location.id` must be set.
1142
+ - On `create()`, the SDK internally converts `location` to `locationId` and strips `id` from holidays. On `update()`, it additionally strips `type` and `level` (immutable after creation).
1143
+ - `call_forward_available_phone_numbers()` lists PSTN numbers available as forwarding destinations for operating modes at a given location.
1144
+ - The `list()` result is sorted ascending by operating mode name.
1145
+
1146
+ ---
1147
+
1148
+ ## 7. Hot Desking Sign-in via Voice Portal
1149
+
1150
+ **SDK module:** `wxc_sdk.telephony.hotdesking_voiceportal`
1151
+ **API class:** `HotDeskingSigninViaVoicePortalApi` (base: `telephony/config`)
1152
+
1153
+ Hot desking allows users to sign in to a shared phone via the voice portal and make calls using their own phone number.
1154
+
1155
+ ### 7.1 Scopes
1156
+
1157
+ | Operation | Scope |
1158
+ |-----------|-------|
1159
+ | Read | `spark-admin:telephony_config_read` |
1160
+ | Write | `spark-admin:telephony_config_write` |
1161
+
1162
+ ### 7.2 Data Models
1163
+
1164
+ #### `HotDeskingVoicePortalSetting`
1165
+
1166
+ | Field | Type | Notes |
1167
+ |-------|------|-------|
1168
+ | `enabled` | `bool` | JSON alias: `voicePortalHotDeskSignInEnabled` |
1169
+
1170
+ Single boolean -- enables or disables hot desking sign-in via voice portal.
1171
+
1172
+ ### 7.3 API Methods
1173
+
1174
+ ```python
1175
+ class HotDeskingSigninViaVoicePortalApi:
1176
+
1177
+ # --- Location level ---
1178
+ def location_get(self, location_id: str, org_id: str = None) -> HotDeskingVoicePortalSetting
1179
+ def location_update(self, location_id: str, setting: HotDeskingVoicePortalSetting, org_id: str = None)
1180
+
1181
+ # --- User level ---
1182
+ def user_get(self, person_id: str, org_id: str = None) -> HotDeskingVoicePortalSetting
1183
+ def user_update(self, person_id: str, setting: HotDeskingVoicePortalSetting, org_id: str = None)
1184
+ ```
1185
+
1186
+ ### 7.4 CLI: `hot-desking-portal`
1187
+
1188
+ | Command | Description |
1189
+ |---------|-------------|
1190
+ | `hot-desking-portal show <location_id>` | Get voice portal hot desking settings for a location |
1191
+ | `hot-desking-portal update <location_id>` | Update voice portal hot desking settings for a location |
1192
+ | `hot-desking-portal show-guest <person_id>` | Get voice portal hot desking settings for a user |
1193
+ | `hot-desking-portal update-guest <person_id>` | Update voice portal hot desking settings for a user |
1194
+
1195
+ ```bash
1196
+ # Get hot desking voice portal settings for a location
1197
+ wxcli hot-desking-portal show <location_id>
1198
+
1199
+ # Enable hot desking sign-in via voice portal for a location
1200
+ wxcli hot-desking-portal update <location_id> --voice-portal-hot-desk-sign-in-enabled
1201
+
1202
+ # Disable hot desking sign-in via voice portal for a location
1203
+ wxcli hot-desking-portal update <location_id> --no-voice-portal-hot-desk-sign-in-enabled
1204
+
1205
+ # Get hot desking guest settings for a specific user
1206
+ wxcli hot-desking-portal show-guest <person_id>
1207
+
1208
+ # Enable a user as a hot desking guest
1209
+ wxcli hot-desking-portal update-guest <person_id> --voice-portal-hot-desk-sign-in-enabled
1210
+
1211
+ # Disable a user as a hot desking guest
1212
+ wxcli hot-desking-portal update-guest <person_id> --no-voice-portal-hot-desk-sign-in-enabled
1213
+ ```
1214
+
1215
+ ### 7.5 Key Behaviors
1216
+
1217
+ - Location endpoint: `locations/{location_id}/features/hotDesking`
1218
+ - User endpoint: `people/{person_id}/features/hotDesking/guest`
1219
+ - This is a simple enable/disable toggle at both location and per-user levels.
1220
+ - The user-level setting controls whether a specific user can act as a hot desking guest.
1221
+
1222
+ ---
1223
+
1224
+ ## 8. Forwarding (Shared Patterns)
1225
+
1226
+ **SDK module:** `wxc_sdk.telephony.forwarding`
1227
+ **API class:** `ForwardingApi` (base: dynamic per feature)
1228
+
1229
+ Shared forwarding settings and selective rules used by **Call Queues**, **Hunt Groups**, and **Auto Attendants**. The `ForwardingApi` is instantiated with a `FeatureSelector` that determines which feature type it operates on.
1230
+
1231
+ ### 8.1 Feature Selectors
1232
+
1233
+ ```python
1234
+ class FeatureSelector(str, Enum):
1235
+ queues = 'queues'
1236
+ huntgroups = 'huntGroups'
1237
+ auto_attendants = 'autoAttendants'
1238
+ ```
1239
+
1240
+ The API endpoint is constructed as:
1241
+ ```
1242
+ telephony/config/locations/{location_id}/{feature}/​{feature_id}/callForwarding[/{path}]
1243
+ ```
1244
+
1245
+ ### 8.2 Data Models
1246
+
1247
+ #### `CallForwarding`
1248
+ Top-level forwarding configuration for a feature.
1249
+
1250
+ | Field | Type | Notes |
1251
+ |-------|------|-------|
1252
+ | `always` | `ForwardingSetting` | Forward all calls |
1253
+ | `selective` | `ForwardingSetting` | Forward based on rules |
1254
+ | `rules` | `list[ForwardingRule]` | Selective forwarding rules |
1255
+ | `operating_modes` | `ForwardOperatingModes` | Mode-based forwarding |
1256
+
1257
+ #### `ForwardingSetting`
1258
+
1259
+ | Field | Type | Notes |
1260
+ |-------|------|-------|
1261
+ | `enabled` | `bool` | |
1262
+ | `destination` | `str` | Forwarding destination number |
1263
+ | `ring_reminder_enabled` | `bool` | Brief tone on forwarded call |
1264
+ | `destination_voice_mail_enabled` | `bool` | Forward to destination's voicemail |
1265
+ | `send_to_voicemail_enabled` | `bool` | Send to voicemail |
1266
+
1267
+ #### `ForwardingRule`
1268
+ Summary of a selective forwarding rule (returned in the rules list).
1269
+
1270
+ | Field | Type |
1271
+ |-------|------|
1272
+ | `id` | `str` |
1273
+ | `name` | `str` |
1274
+ | `calls_from` | `str` |
1275
+ | `forward_to` | `str` |
1276
+ | `calls_to` | `str` |
1277
+ | `enabled` | `bool` |
1278
+
1279
+ #### `ForwardingRuleDetails`
1280
+ Full details of a selective forwarding rule.
1281
+
1282
+ | Field | Type | Notes |
1283
+ |-------|------|-------|
1284
+ | `name` | `str` | |
1285
+ | `id` | `str` | |
1286
+ | `enabled` | `bool` | |
1287
+ | `holiday_schedule` | `str` | Schedule name for when rule applies |
1288
+ | `business_schedule` | `str` | Schedule name for when rule applies |
1289
+ | `forward_to` | `ForwardTo` | |
1290
+ | `calls_to` | `ForwardCallsTo` | List of numbers/extensions |
1291
+ | `calls_from` | `CallsFrom` | |
1292
+
1293
+ #### `ForwardTo`
1294
+
1295
+ | Field | Type | Default |
1296
+ |-------|------|---------|
1297
+ | `selection` | `ForwardToSelection` | `FORWARD_TO_DEFAULT_NUMBER` |
1298
+ | `phone_number` | `str` | |
1299
+
1300
+ #### `ForwardToSelection` (Enum)
1301
+
1302
+ | Value | Meaning |
1303
+ |-------|---------|
1304
+ | `FORWARD_TO_DEFAULT_NUMBER` | Use default forwarding number |
1305
+ | `FORWARD_TO_SPECIFIED_NUMBER` | Use number in `phone_number` field |
1306
+ | `DO_NOT_FORWARD` | Do not forward |
1307
+
1308
+ #### `ForwardFromSelection` (Enum)
1309
+
1310
+ | Value | Meaning |
1311
+ |-------|---------|
1312
+ | `ANY` | Match any caller |
1313
+ | `CUSTOM` | Match specific callers only |
1314
+
1315
+ #### `CallsFrom`
1316
+
1317
+ | Field | Type | Default |
1318
+ |-------|------|---------|
1319
+ | `selection` | `ForwardFromSelection` | `ANY` |
1320
+ | `custom_numbers` | `CustomNumbers` | |
1321
+
1322
+ #### `CustomNumbers`
1323
+
1324
+ | Field | Type | Default |
1325
+ |-------|------|---------|
1326
+ | `private_number_enabled` | `bool` | `False` |
1327
+ | `unavailable_number_enabled` | `bool` | `False` |
1328
+ | `numbers` | `list[str]` | |
1329
+
1330
+ #### `ForwardOperatingModes`
1331
+
1332
+ | Field | Type | Notes |
1333
+ |-------|------|-------|
1334
+ | `enabled` | `bool` | Operating modes enabled |
1335
+ | `current_operating_mode_id` | `str` | Currently active mode |
1336
+ | `exception_type` | `ExceptionType` | |
1337
+ | `modes` | `list[ModeForward]` | Configured modes |
1338
+
1339
+ #### `ModeForward`
1340
+
1341
+ | Field | Type |
1342
+ |-------|------|
1343
+ | `normal_operation_enabled` | `bool` |
1344
+ | `id` | `str` |
1345
+ | `name` | `str` |
1346
+ | `type` | `OperatingModeSchedule` |
1347
+ | `level` | `ScheduleLevel` |
1348
+ | `forward_to` | `ModeForwardTo` |
1349
+
1350
+ #### `ModeForwardTo`
1351
+
1352
+ | Field | Type | Notes |
1353
+ |-------|------|-------|
1354
+ | `selection` | `ForwardToSelection` | |
1355
+ | `destination` | `str` | Required when selection is `FORWARD_TO_SPECIFIED_NUMBER` |
1356
+ | `destination_voicemail_enabled` | `bool` | |
1357
+ | `default_destination` | `str` | Operating mode's own destination |
1358
+ | `default_destination_voicemail_enabled` | `bool` | |
1359
+ | `default_forward_to_selection` | `ModeDefaultForwardToSelection` | |
1360
+
1361
+ ### 8.3 API Methods
1362
+
1363
+ ```python
1364
+ class ForwardingApi:
1365
+
1366
+ def __init__(self, session: RestSession, feature_selector: FeatureSelector)
1367
+
1368
+ # --- Get/Update forwarding settings ---
1369
+ def settings(self, location_id: str, feature_id: str, org_id: str = None) -> CallForwarding
1370
+ def update(self, location_id: str, feature_id: str,
1371
+ forwarding: CallForwarding, org_id: str = None)
1372
+
1373
+ # --- Selective forwarding rules ---
1374
+ def create_call_forwarding_rule(self, location_id: str, feature_id: str,
1375
+ forwarding_rule: ForwardingRuleDetails,
1376
+ org_id: str = None) -> str # returns rule ID
1377
+
1378
+ def call_forwarding_rule(self, location_id: str, feature_id: str,
1379
+ rule_id: str, org_id: str = None) -> ForwardingRuleDetails
1380
+
1381
+ def update_call_forwarding_rule(self, location_id: str, feature_id: str,
1382
+ rule_id: str, forwarding_rule: ForwardingRuleDetails,
1383
+ org_id: str = None) -> str # returns new rule ID
1384
+
1385
+ def delete_call_forwarding_rule(self, location_id: str, feature_id: str,
1386
+ rule_id: str, org_id: str = None)
1387
+
1388
+ # --- Operating mode switch ---
1389
+ def switch_mode_for_call_forwarding(self, location_id: str, feature_id: str,
1390
+ org_id: str = None)
1391
+ ```
1392
+
1393
+ ### 8.4 CLI Examples
1394
+
1395
+ Forwarding commands are accessed through the feature-specific command groups (`auto-attendant`, `call-queue`, `hunt-group`), not a standalone forwarding group. The commands follow the same pattern across all three features:
1396
+
1397
+ ```bash
1398
+ # --- Call Queue forwarding ---
1399
+
1400
+ # Get call forwarding settings for a call queue
1401
+ wxcli call-queue show-call-forwarding <location_id> <queue_id>
1402
+
1403
+ # Update call forwarding settings (use --json-body for complex forwarding rules)
1404
+ wxcli call-queue update-call-forwarding <location_id> <queue_id> --json-body '{
1405
+ "callForwarding": {
1406
+ "always": {"enabled": true, "destination": "+14155551234"}
1407
+ }
1408
+ }'
1409
+
1410
+ # Create a selective call forwarding rule
1411
+ wxcli call-queue create-selective-rules <location_id> <queue_id> \
1412
+ --name "After Hours Forward" --enabled \
1413
+ --business-schedule "After Hours Schedule"
1414
+
1415
+ # Get a selective call forwarding rule
1416
+ wxcli call-queue show-selective-rules <location_id> <queue_id> <rule_id>
1417
+
1418
+ # Delete a selective call forwarding rule
1419
+ wxcli call-queue delete-selective-rules <location_id> <queue_id> <rule_id>
1420
+
1421
+ # Get available phone numbers for call forwarding
1422
+ wxcli call-queue list-available-numbers-call-forwarding <location_id> <queue_id>
1423
+
1424
+ # Switch operating mode back to normal operations
1425
+ wxcli call-queue switch-mode-for <location_id> <queue_id>
1426
+
1427
+ # --- Auto Attendant forwarding (same pattern) ---
1428
+ wxcli auto-attendant show-call-forwarding <location_id> <aa_id>
1429
+ wxcli auto-attendant create-selective-rules <location_id> <aa_id> --name "Holiday Forward"
1430
+ wxcli auto-attendant switch-mode-for <location_id> <aa_id>
1431
+
1432
+ # --- Hunt Group forwarding (same pattern) ---
1433
+ wxcli hunt-group show-call-forwarding <location_id> <hg_id>
1434
+ wxcli hunt-group create-selective-rules <location_id> <hg_id> --name "Overflow Forward"
1435
+ wxcli hunt-group switch-mode-for <location_id> <hg_id>
1436
+ ```
1437
+
1438
+ ### 8.5 Key Behaviors
1439
+
1440
+ - **Rule ID changes on rename**: The Call Forwarding Rule ID will change when the rule name is modified via `update_call_forwarding_rule()`. The new ID is returned.
1441
+ - **NANP number normalization**: The SDK automatically handles +1 prefix transformations. Numbers returned from the platform without a `+` prefix get `+1-` prepended. When serializing for API calls, `+1-` is stripped back off. Non-NANP numbers (those starting with `+`) are left as-is.
1442
+ - **`switch_mode_for_call_forwarding()`** switches the feature's current operating mode back to normal operations.
1443
+ - The `update()` method serializes with `exclude_unset=True`, so only fields you explicitly set are sent to the API. It also strips read-only fields (`calls_from`, `forward_to`, `calls_to`, `name`) from rule summaries in the rules list.
1444
+ - **`CallForwarding.default()`** provides a safe starting point: always-forward disabled, selective disabled, empty rules list.
1445
+ - Three forwarding modes are available simultaneously: always forward, selective rules, and operating mode-based. The API returns all three as part of the `CallForwarding` object.
1446
+
1447
+ ---
1448
+
1449
+ ## Cross-Reference: Scope Summary
1450
+
1451
+ | API | Read Scope | Write Scope |
1452
+ |-----|-----------|-------------|
1453
+ | Call Recording | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` |
1454
+ | Caller Reputation | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` |
1455
+ | Conference Controls | `spark:calls_read` | `spark:calls_write` |
1456
+ | Supervisors | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` |
1457
+ | Guest Calling | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` |
1458
+ | Operating Modes | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` |
1459
+ | Hot Desking Voice Portal | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` |
1460
+ | Forwarding | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` |
1461
+
1462
+ Note that Conference Controls uses **user-level** scopes (`spark:calls_*`), not admin scopes, because it is a runtime call-control API.
1463
+
1464
+ ---
1465
+
1466
+ ## Gotchas (Cross-Cutting)
1467
+
1468
+ - **Conference Controls require user-level OAuth.** Unlike all other APIs in this doc, Conference Controls uses `spark:calls_read` / `spark:calls_write` scopes (not admin scopes). Admin or service-app tokens will fail. This matches the `call-controls` known issue documented in CLAUDE.md.
1469
+ - **Caller Reputation uses `organizationId`, not `orgId`.** Most telephony APIs use `orgId` as the query parameter, but the caller reputation endpoints use `organizationId`. The wxcli maps this as `--organization-id`.
1470
+ - **Call Recording vendor changes are async.** `set_org_vendor()` and `set_location_vendor()` may return a job ID (HTTP 200) instead of completing immediately (HTTP 204). Always check job status via `wxcli call-recording show-call-recording <job_id>` after vendor changes.
1471
+ - **Operating mode type and level are immutable after creation.** You cannot change `type` (SAME_HOURS_DAILY, DIFFERENT_HOURS_DAILY, HOLIDAY) or `level` (ORGANIZATION, LOCATION) after creating an operating mode. Delete and recreate if you need to change these.
1472
+ - **Forwarding rule IDs change on rename.** When you rename a selective forwarding rule via `update_call_forwarding_rule()`, the rule ID changes. The new ID is returned from the update call. Store it if you need to reference the rule again.
1473
+ - **Score thresholds are strings.** The caller reputation `callBlockScoreThreshold` and `callAllowScoreThreshold` fields are string-typed, not integers. Pass them as quoted values in both CLI and raw HTTP.
1474
+ - **`delete_bulk` supervisors has a nuke option.** Setting `delete_all: true` on `delete_bulk()` ignores the provided ID list and removes ALL supervisors in the org. Double-check before using.
1475
+ - **Hot desking has two levels.** The location setting enables the feature globally for the location; the user-level setting controls whether a specific user can act as a hot desking guest. Both must be enabled for a user to hot-desk at that location.
1476
+ - **TimePeriod AXL field constraints.** The `monthOfYear` field uses 3-letter abbreviations only (`Dec`, `Jan`, `Feb`, etc., not full names like `December`). The `startTime`/`endTime` fields only accept on-the-hour values per the `TypeTimeOfDay` enum — values like `23:59` or `17:30` are rejected. For date-specific (holiday) time periods, use `monthOfYear` + `dayOfMonth` with valid time range (e.g., `08:00`–`17:00`).
1477
+
1478
+ ---
1479
+
1480
+ ## See Also
1481
+
1482
+ - **[Person Call Settings — Media](person-call-settings-media.md)** — Per-user call recording settings (record mode, notifications, access permissions) that work within the vendor/compliance framework configured here