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,3347 @@
1
+ import json
2
+ import typer
3
+ from wxcli.auth import get_api
4
+ from wxcli.errors import WebexError, handle_rest_error
5
+ from wxcli.output import print_table, print_json
6
+ from wxcli.config import get_org_id
7
+
8
+
9
+ app = typer.Typer(help="Manage Webex Calling workspace-settings.")
10
+
11
+
12
+ @app.command("show")
13
+ def show(
14
+ workspace_id: str = typer.Argument(help="workspaceId"),
15
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
16
+ debug: bool = typer.Option(False, "--debug"),
17
+ ):
18
+ """Retrieve Call Forwarding Settings for a Workspace."""
19
+ api = get_api(debug=debug)
20
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/callForwarding"
21
+ params = {}
22
+ org_id = get_org_id()
23
+ if org_id is not None:
24
+ params["orgId"] = org_id
25
+ try:
26
+ result = api.session.rest_get(url, params=params)
27
+ except WebexError as e:
28
+ handle_rest_error(e)
29
+ if output == "json":
30
+ print_json(result)
31
+ else:
32
+ if isinstance(result, dict):
33
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
34
+ elif isinstance(result, list):
35
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
36
+ else:
37
+ print_json(result)
38
+
39
+
40
+
41
+ @app.command("update")
42
+ def update(
43
+ workspace_id: str = typer.Argument(help="workspaceId"),
44
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
45
+ debug: bool = typer.Option(False, "--debug"),
46
+ ):
47
+ """Modify Call Forwarding Settings for a Workspace\n\nExample --json-body:\n '{"callForwarding":{"always":{"enabled":"...","ringReminderEnabled":"...","destinationVoicemailEnabled":"...","destination":"..."},"busy":{"enabled":"...","destinationVoicemailEnabled":"...","destination":"..."},"noAnswer":{"enabled":"...","destination":"...","numberOfRings":"...","systemMaxNumberOfRings":"...","destinationVoicemailEnabled":"..."}},"businessContinuity":{"enabled":true,"destinationVoicemailEnabled":true,"destination":"..."}}'."""
48
+ api = get_api(debug=debug)
49
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/callForwarding"
50
+ params = {}
51
+ org_id = get_org_id()
52
+ if org_id is not None:
53
+ params["orgId"] = org_id
54
+ if json_body:
55
+ body = json.loads(json_body)
56
+ else:
57
+ body = {}
58
+ try:
59
+ result = api.session.rest_put(url, json=body, params=params)
60
+ except WebexError as e:
61
+ handle_rest_error(e)
62
+ typer.echo(f"Updated.")
63
+
64
+
65
+
66
+ @app.command("show-call-waiting")
67
+ def show_call_waiting(
68
+ workspace_id: str = typer.Argument(help="workspaceId"),
69
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
70
+ debug: bool = typer.Option(False, "--debug"),
71
+ ):
72
+ """Retrieve Call Waiting Settings for a Workspace."""
73
+ api = get_api(debug=debug)
74
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/callWaiting"
75
+ params = {}
76
+ org_id = get_org_id()
77
+ if org_id is not None:
78
+ params["orgId"] = org_id
79
+ try:
80
+ result = api.session.rest_get(url, params=params)
81
+ except WebexError as e:
82
+ handle_rest_error(e)
83
+ if output == "json":
84
+ print_json(result)
85
+ else:
86
+ if isinstance(result, dict):
87
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
88
+ elif isinstance(result, list):
89
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
90
+ else:
91
+ print_json(result)
92
+
93
+
94
+
95
+ @app.command("update-call-waiting")
96
+ def update_call_waiting(
97
+ workspace_id: str = typer.Argument(help="workspaceId"),
98
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Call Waiting state."),
99
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
100
+ debug: bool = typer.Option(False, "--debug"),
101
+ ):
102
+ """Modify Call Waiting Settings for a Workspace."""
103
+ api = get_api(debug=debug)
104
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/callWaiting"
105
+ params = {}
106
+ org_id = get_org_id()
107
+ if org_id is not None:
108
+ params["orgId"] = org_id
109
+ if json_body:
110
+ body = json.loads(json_body)
111
+ else:
112
+ body = {}
113
+ if enabled is not None:
114
+ body["enabled"] = enabled
115
+ try:
116
+ result = api.session.rest_put(url, json=body, params=params)
117
+ except WebexError as e:
118
+ handle_rest_error(e)
119
+ typer.echo(f"Updated.")
120
+
121
+
122
+
123
+ @app.command("list")
124
+ def cmd_list(
125
+ workspace_id: str = typer.Argument(help="workspaceId"),
126
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
127
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
128
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
129
+ debug: bool = typer.Option(False, "--debug"),
130
+ ):
131
+ """Read Caller ID Settings for a Workspace."""
132
+ api = get_api(debug=debug)
133
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/callerId"
134
+ params = {}
135
+ if limit > 0:
136
+ params["max"] = limit
137
+ if offset > 0:
138
+ params["start"] = offset
139
+ org_id = get_org_id()
140
+ if org_id is not None:
141
+ params["orgId"] = org_id
142
+ result = None
143
+ try:
144
+ result = api.session.rest_get(url, params=params)
145
+ except WebexError as e:
146
+ handle_rest_error(e)
147
+ result = result or []
148
+ items = result.get("types", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
149
+ if output == "json":
150
+ print_json(items)
151
+ else:
152
+ print_table(items, columns=[('Number', 'directNumber'), ('Extension', 'extension')], limit=limit)
153
+
154
+
155
+
156
+ @app.command("update-caller-id")
157
+ def update_caller_id(
158
+ workspace_id: str = typer.Argument(help="workspaceId"),
159
+ selected: str = typer.Option(None, "--selected", help="Choices: DIRECT_LINE, LOCATION_NUMBER, CUSTOM"),
160
+ custom_number: str = typer.Option(None, "--custom-number", help="Custom number which is shown if CUSTOM is selected. This val"),
161
+ display_name: str = typer.Option(None, "--display-name", help="Workspace's caller ID display name. This field has been depr"),
162
+ display_detail: str = typer.Option(None, "--display-detail", help="Workspace's caller ID display details. This field has been d"),
163
+ block_in_forward_calls_enabled: bool = typer.Option(None, "--block-in-forward-calls-enabled/--no-block-in-forward-calls-enabled", help="Block this workspace's identity when receiving a call."),
164
+ external_caller_id_name_policy: str = typer.Option(None, "--external-caller-id-name-policy", help="Choices: DIRECT_LINE, LOCATION, OTHER"),
165
+ custom_external_caller_id_name: str = typer.Option(None, "--custom-external-caller-id-name", help="Custom external caller ID name which is shown if external ca"),
166
+ location_external_caller_id_name: str = typer.Option(None, "--location-external-caller-id-name", help="Location's external caller ID name which is shown if externa"),
167
+ dial_by_name: str = typer.Option(None, "--dial-by-name", help="Sets or clears the name to be used for dial by name function"),
168
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
169
+ debug: bool = typer.Option(False, "--debug"),
170
+ ):
171
+ """Configure Caller ID Settings for a Workspace\n\nExample --json-body:\n '{"selected":"DIRECT_LINE","customNumber":"...","displayName":"...","displayDetail":"...","blockInForwardCallsEnabled":true,"externalCallerIdNamePolicy":"DIRECT_LINE","customExternalCallerIdName":"...","locationExternalCallerIdName":"..."}'."""
172
+ api = get_api(debug=debug)
173
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/callerId"
174
+ params = {}
175
+ org_id = get_org_id()
176
+ if org_id is not None:
177
+ params["orgId"] = org_id
178
+ if json_body:
179
+ body = json.loads(json_body)
180
+ else:
181
+ body = {}
182
+ if selected is not None:
183
+ body["selected"] = selected
184
+ if custom_number is not None:
185
+ body["customNumber"] = custom_number
186
+ if display_name is not None:
187
+ body["displayName"] = display_name
188
+ if display_detail is not None:
189
+ body["displayDetail"] = display_detail
190
+ if block_in_forward_calls_enabled is not None:
191
+ body["blockInForwardCallsEnabled"] = block_in_forward_calls_enabled
192
+ if external_caller_id_name_policy is not None:
193
+ body["externalCallerIdNamePolicy"] = external_caller_id_name_policy
194
+ if custom_external_caller_id_name is not None:
195
+ body["customExternalCallerIdName"] = custom_external_caller_id_name
196
+ if location_external_caller_id_name is not None:
197
+ body["locationExternalCallerIdName"] = location_external_caller_id_name
198
+ if dial_by_name is not None:
199
+ body["dialByName"] = dial_by_name
200
+ try:
201
+ result = api.session.rest_put(url, json=body, params=params)
202
+ except WebexError as e:
203
+ handle_rest_error(e)
204
+ typer.echo(f"Updated.")
205
+
206
+
207
+
208
+ @app.command("list-monitoring")
209
+ def list_monitoring(
210
+ workspace_id: str = typer.Argument(help="workspaceId"),
211
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
212
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
213
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
214
+ debug: bool = typer.Option(False, "--debug"),
215
+ ):
216
+ """Retrieve Monitoring Settings for a Workspace."""
217
+ api = get_api(debug=debug)
218
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/monitoring"
219
+ params = {}
220
+ if limit > 0:
221
+ params["max"] = limit
222
+ if offset > 0:
223
+ params["start"] = offset
224
+ org_id = get_org_id()
225
+ if org_id is not None:
226
+ params["orgId"] = org_id
227
+ result = None
228
+ try:
229
+ result = api.session.rest_get(url, params=params)
230
+ except WebexError as e:
231
+ handle_rest_error(e)
232
+ result = result or []
233
+ items = result.get("monitoredElements", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
234
+ if output == "json":
235
+ print_json(items)
236
+ else:
237
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
238
+
239
+
240
+
241
+ @app.command("update-monitoring")
242
+ def update_monitoring(
243
+ workspace_id: str = typer.Argument(help="workspaceId"),
244
+ enable_call_park_notification: bool = typer.Option(None, "--enable-call-park-notification/--no-enable-call-park-notification", help="Call park notification is enabled or disabled."),
245
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
246
+ debug: bool = typer.Option(False, "--debug"),
247
+ ):
248
+ """Modify Monitoring Settings for a Workspace\n\nExample --json-body:\n '{"enableCallParkNotification":true,"monitoredElements":[{"id":"...","type":"...","lineKeyLabel":"...","phoneNumber":"..."}]}'."""
249
+ api = get_api(debug=debug)
250
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/monitoring"
251
+ params = {}
252
+ org_id = get_org_id()
253
+ if org_id is not None:
254
+ params["orgId"] = org_id
255
+ if json_body:
256
+ body = json.loads(json_body)
257
+ else:
258
+ body = {}
259
+ if enable_call_park_notification is not None:
260
+ body["enableCallParkNotification"] = enable_call_park_notification
261
+ try:
262
+ result = api.session.rest_put(url, json=body, params=params)
263
+ except WebexError as e:
264
+ handle_rest_error(e)
265
+ typer.echo(f"Updated.")
266
+
267
+
268
+
269
+ @app.command("list-available-members-speed-dials")
270
+ def list_available_members_speed_dials(
271
+ workspace_id: str = typer.Argument(help="workspaceId"),
272
+ location_id: str = typer.Option(None, "--location-id", help="Search for the available speed dials in the location ID."),
273
+ member_name: str = typer.Option(None, "--member-name", help="Search for available members by name."),
274
+ phone_number: str = typer.Option(None, "--phone-number", help="Search for available members by number or extension."),
275
+ order: str = typer.Option(None, "--order", help="Sort response based on `firstName` or `lastName` with sort d"),
276
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
277
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
278
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
279
+ debug: bool = typer.Option(False, "--debug"),
280
+ ):
281
+ """Get Available Speed Dials for Workspace Monitoring."""
282
+ api = get_api(debug=debug)
283
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/monitoring/speedDials/availableMembers"
284
+ params = {}
285
+ if location_id is not None:
286
+ params["locationId"] = location_id
287
+ if member_name is not None:
288
+ params["memberName"] = member_name
289
+ if phone_number is not None:
290
+ params["phoneNumber"] = phone_number
291
+ if order is not None:
292
+ params["order"] = order
293
+ if limit > 0:
294
+ params["max"] = limit
295
+ if offset > 0:
296
+ params["start"] = offset
297
+ org_id = get_org_id()
298
+ if org_id is not None:
299
+ params["orgId"] = org_id
300
+ result = None
301
+ try:
302
+ result = api.session.rest_get(url, params=params)
303
+ except WebexError as e:
304
+ handle_rest_error(e)
305
+ result = result or []
306
+ items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
307
+ if output == "json":
308
+ print_json(items)
309
+ else:
310
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
311
+
312
+
313
+
314
+ @app.command("list-available-members-monitoring")
315
+ def list_available_members_monitoring(
316
+ workspace_id: str = typer.Argument(help="workspaceId"),
317
+ location_id: str = typer.Option(None, "--location-id", help="Search for the available members in the location ID."),
318
+ member_name: str = typer.Option(None, "--member-name", help="Search for available members by name."),
319
+ phone_number: str = typer.Option(None, "--phone-number", help="Search for available members by number or extension."),
320
+ order: str = typer.Option(None, "--order", help="Sort response based on `firstName` or `lastName` with sort d"),
321
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
322
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
323
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
324
+ debug: bool = typer.Option(False, "--debug"),
325
+ ):
326
+ """Get Available Members for Workspace Monitoring."""
327
+ api = get_api(debug=debug)
328
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/monitoring/availableMembers"
329
+ params = {}
330
+ if location_id is not None:
331
+ params["locationId"] = location_id
332
+ if member_name is not None:
333
+ params["memberName"] = member_name
334
+ if phone_number is not None:
335
+ params["phoneNumber"] = phone_number
336
+ if order is not None:
337
+ params["order"] = order
338
+ if limit > 0:
339
+ params["max"] = limit
340
+ if offset > 0:
341
+ params["start"] = offset
342
+ org_id = get_org_id()
343
+ if org_id is not None:
344
+ params["orgId"] = org_id
345
+ result = None
346
+ try:
347
+ result = api.session.rest_get(url, params=params)
348
+ except WebexError as e:
349
+ handle_rest_error(e)
350
+ result = result or []
351
+ items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
352
+ if output == "json":
353
+ print_json(items)
354
+ else:
355
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
356
+
357
+
358
+
359
+ @app.command("list-numbers")
360
+ def list_numbers(
361
+ workspace_id: str = typer.Argument(help="workspaceId"),
362
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
363
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
364
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
365
+ debug: bool = typer.Option(False, "--debug"),
366
+ ):
367
+ """List numbers associated with a specific workspace."""
368
+ api = get_api(debug=debug)
369
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/numbers"
370
+ params = {}
371
+ if limit > 0:
372
+ params["max"] = limit
373
+ if offset > 0:
374
+ params["start"] = offset
375
+ org_id = get_org_id()
376
+ if org_id is not None:
377
+ params["orgId"] = org_id
378
+ result = None
379
+ try:
380
+ result = api.session.rest_get(url, params=params)
381
+ except WebexError as e:
382
+ handle_rest_error(e)
383
+ result = result or []
384
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
385
+ if output == "json":
386
+ print_json(items)
387
+ else:
388
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
389
+
390
+
391
+
392
+ @app.command("show-incoming-permission")
393
+ def show_incoming_permission(
394
+ workspace_id: str = typer.Argument(help="workspaceId"),
395
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
396
+ debug: bool = typer.Option(False, "--debug"),
397
+ ):
398
+ """Retrieve Incoming Permission Settings for a Workspace."""
399
+ api = get_api(debug=debug)
400
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/incomingPermission"
401
+ params = {}
402
+ org_id = get_org_id()
403
+ if org_id is not None:
404
+ params["orgId"] = org_id
405
+ try:
406
+ result = api.session.rest_get(url, params=params)
407
+ except WebexError as e:
408
+ handle_rest_error(e)
409
+ if output == "json":
410
+ print_json(result)
411
+ else:
412
+ if isinstance(result, dict):
413
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
414
+ elif isinstance(result, list):
415
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
416
+ else:
417
+ print_json(result)
418
+
419
+
420
+
421
+ @app.command("update-incoming-permission")
422
+ def update_incoming_permission(
423
+ workspace_id: str = typer.Argument(help="workspaceId"),
424
+ use_custom_enabled: bool = typer.Option(None, "--use-custom-enabled/--no-use-custom-enabled", help="Incoming Permission state. If disabled, the default settings"),
425
+ external_transfer: str = typer.Option(None, "--external-transfer", help="Choices: ALLOW_ALL_EXTERNAL, ALLOW_ONLY_TRANSFERRED_EXTERNAL, BLOCK_ALL_EXTERNAL"),
426
+ internal_calls_enabled: bool = typer.Option(None, "--internal-calls-enabled/--no-internal-calls-enabled", help="Flag to indicate if the workspace can receive internal calls"),
427
+ collect_calls_enabled: bool = typer.Option(None, "--collect-calls-enabled/--no-collect-calls-enabled", help="Flag to indicate if the workspace can receive collect calls."),
428
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
429
+ debug: bool = typer.Option(False, "--debug"),
430
+ ):
431
+ """Modify Incoming Permission Settings for a Workspace."""
432
+ api = get_api(debug=debug)
433
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/incomingPermission"
434
+ params = {}
435
+ org_id = get_org_id()
436
+ if org_id is not None:
437
+ params["orgId"] = org_id
438
+ if json_body:
439
+ body = json.loads(json_body)
440
+ else:
441
+ body = {}
442
+ if use_custom_enabled is not None:
443
+ body["useCustomEnabled"] = use_custom_enabled
444
+ if external_transfer is not None:
445
+ body["externalTransfer"] = external_transfer
446
+ if internal_calls_enabled is not None:
447
+ body["internalCallsEnabled"] = internal_calls_enabled
448
+ if collect_calls_enabled is not None:
449
+ body["collectCallsEnabled"] = collect_calls_enabled
450
+ try:
451
+ result = api.session.rest_put(url, json=body, params=params)
452
+ except WebexError as e:
453
+ handle_rest_error(e)
454
+ typer.echo(f"Updated.")
455
+
456
+
457
+
458
+ @app.command("list-outgoing-permission")
459
+ def list_outgoing_permission(
460
+ workspace_id: str = typer.Argument(help="workspaceId"),
461
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
462
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
463
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
464
+ debug: bool = typer.Option(False, "--debug"),
465
+ ):
466
+ """Retrieve Outgoing Permission Settings for a Workspace."""
467
+ api = get_api(debug=debug)
468
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/outgoingPermission"
469
+ params = {}
470
+ if limit > 0:
471
+ params["max"] = limit
472
+ if offset > 0:
473
+ params["start"] = offset
474
+ org_id = get_org_id()
475
+ if org_id is not None:
476
+ params["orgId"] = org_id
477
+ result = None
478
+ try:
479
+ result = api.session.rest_get(url, params=params)
480
+ except WebexError as e:
481
+ handle_rest_error(e)
482
+ result = result or []
483
+ items = result.get("callingPermissions", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
484
+ if output == "json":
485
+ print_json(items)
486
+ else:
487
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
488
+
489
+
490
+
491
+ @app.command("update-outgoing-permission")
492
+ def update_outgoing_permission(
493
+ workspace_id: str = typer.Argument(help="workspaceId"),
494
+ use_custom_enabled: bool = typer.Option(None, "--use-custom-enabled/--no-use-custom-enabled", help="When `true`, indicates that this workspace uses the shared c"),
495
+ use_custom_permissions: bool = typer.Option(None, "--use-custom-permissions/--no-use-custom-permissions", help="When `true`, indicates that this workspace uses the specifie"),
496
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
497
+ debug: bool = typer.Option(False, "--debug"),
498
+ ):
499
+ """Modify Outgoing Permission Settings for a Workspace\n\nExample --json-body:\n '{"useCustomEnabled":true,"useCustomPermissions":true,"callingPermissions":[{"callType":"...","action":"...","transferEnabled":"..."}]}'."""
500
+ api = get_api(debug=debug)
501
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/outgoingPermission"
502
+ params = {}
503
+ org_id = get_org_id()
504
+ if org_id is not None:
505
+ params["orgId"] = org_id
506
+ if json_body:
507
+ body = json.loads(json_body)
508
+ else:
509
+ body = {}
510
+ if use_custom_enabled is not None:
511
+ body["useCustomEnabled"] = use_custom_enabled
512
+ if use_custom_permissions is not None:
513
+ body["useCustomPermissions"] = use_custom_permissions
514
+ try:
515
+ result = api.session.rest_put(url, json=body, params=params)
516
+ except WebexError as e:
517
+ handle_rest_error(e)
518
+ typer.echo(f"Updated.")
519
+
520
+
521
+
522
+ @app.command("list-access-codes")
523
+ def list_access_codes(
524
+ workspace_id: str = typer.Argument(help="workspaceId"),
525
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
526
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
527
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
528
+ debug: bool = typer.Option(False, "--debug"),
529
+ ):
530
+ """Retrieve Access Codes for a Workspace."""
531
+ api = get_api(debug=debug)
532
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/outgoingPermission/accessCodes"
533
+ params = {}
534
+ if limit > 0:
535
+ params["max"] = limit
536
+ if offset > 0:
537
+ params["start"] = offset
538
+ org_id = get_org_id()
539
+ if org_id is not None:
540
+ params["orgId"] = org_id
541
+ result = None
542
+ try:
543
+ result = api.session.rest_get(url, params=params)
544
+ except WebexError as e:
545
+ handle_rest_error(e)
546
+ result = result or []
547
+ items = result.get("accessCodes", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
548
+ if output == "json":
549
+ print_json(items)
550
+ else:
551
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
552
+
553
+
554
+
555
+ @app.command("create")
556
+ def create(
557
+ workspace_id: str = typer.Argument(help="workspaceId"),
558
+ code: str = typer.Option(None, "--code", help="(required) An Access code."),
559
+ description: str = typer.Option(None, "--description", help="(required) The description of the access code."),
560
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
561
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
562
+ debug: bool = typer.Option(False, "--debug"),
563
+ ):
564
+ """Create Access Codes for a Workspace."""
565
+ api = get_api(debug=debug)
566
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/outgoingPermission/accessCodes"
567
+ params = {}
568
+ org_id = get_org_id()
569
+ if org_id is not None:
570
+ params["orgId"] = org_id
571
+ if json_body:
572
+ body = json.loads(json_body)
573
+ else:
574
+ body = {}
575
+ if code is not None:
576
+ body["code"] = code
577
+ if description is not None:
578
+ body["description"] = description
579
+ _missing = [f for f in ['code', 'description'] if f not in body or body[f] is None]
580
+ if _missing:
581
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
582
+ raise typer.Exit(1)
583
+ try:
584
+ result = api.session.rest_post(url, json=body, params=params)
585
+ except WebexError as e:
586
+ handle_rest_error(e)
587
+ if output == "json":
588
+ print_json(result)
589
+ elif isinstance(result, dict) and "id" in result:
590
+ typer.echo(f"Created: {result['id']}")
591
+ elif not result or result == {}:
592
+ typer.echo("Created.")
593
+ else:
594
+ print_json(result)
595
+
596
+
597
+
598
+ @app.command("update-access-codes")
599
+ def update_access_codes(
600
+ workspace_id: str = typer.Argument(help="workspaceId"),
601
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
602
+ debug: bool = typer.Option(False, "--debug"),
603
+ ):
604
+ """Modify Access Codes for a Workspace\n\nExample --json-body:\n '{"deleteCodes":["..."]}'."""
605
+ api = get_api(debug=debug)
606
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/outgoingPermission/accessCodes"
607
+ params = {}
608
+ org_id = get_org_id()
609
+ if org_id is not None:
610
+ params["orgId"] = org_id
611
+ if json_body:
612
+ body = json.loads(json_body)
613
+ else:
614
+ body = {}
615
+ try:
616
+ result = api.session.rest_put(url, json=body, params=params)
617
+ except WebexError as e:
618
+ handle_rest_error(e)
619
+ typer.echo(f"Updated.")
620
+
621
+
622
+
623
+ @app.command("delete")
624
+ def delete(
625
+ workspace_id: str = typer.Argument(help="workspaceId"),
626
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
627
+ debug: bool = typer.Option(False, "--debug"),
628
+ ):
629
+ """Delete all Access Codes for a Workspace."""
630
+ if not force:
631
+ typer.confirm(f"Delete {workspace_id}?", abort=True)
632
+ api = get_api(debug=debug)
633
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/outgoingPermission/accessCodes"
634
+ params = {}
635
+ org_id = get_org_id()
636
+ if org_id is not None:
637
+ params["orgId"] = org_id
638
+ try:
639
+ api.session.rest_delete(url, params=params)
640
+ except WebexError as e:
641
+ handle_rest_error(e)
642
+ typer.echo(f"Deleted: {workspace_id}")
643
+
644
+
645
+
646
+ @app.command("show-intercept")
647
+ def show_intercept(
648
+ workspace_id: str = typer.Argument(help="workspaceId"),
649
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
650
+ debug: bool = typer.Option(False, "--debug"),
651
+ ):
652
+ """Read Call Intercept Settings for a Workspace."""
653
+ api = get_api(debug=debug)
654
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/intercept"
655
+ params = {}
656
+ org_id = get_org_id()
657
+ if org_id is not None:
658
+ params["orgId"] = org_id
659
+ try:
660
+ result = api.session.rest_get(url, params=params)
661
+ except WebexError as e:
662
+ handle_rest_error(e)
663
+ if output == "json":
664
+ print_json(result)
665
+ else:
666
+ if isinstance(result, dict):
667
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
668
+ elif isinstance(result, list):
669
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
670
+ else:
671
+ print_json(result)
672
+
673
+
674
+
675
+ @app.command("update-intercept")
676
+ def update_intercept(
677
+ workspace_id: str = typer.Argument(help="workspaceId"),
678
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if call interception is enabled."),
679
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
680
+ debug: bool = typer.Option(False, "--debug"),
681
+ ):
682
+ """Configure Call Intercept Settings for a Workspace\n\nExample --json-body:\n '{"enabled":true,"incoming":{"type":"INTERCEPT_ALL","voicemailEnabled":true,"announcements":{"greeting":"...","newNumber":"...","zeroTransfer":"..."}},"outgoing":{"type":"INTERCEPT_ALL","transferEnabled":true,"destination":"..."}}'."""
683
+ api = get_api(debug=debug)
684
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/intercept"
685
+ params = {}
686
+ org_id = get_org_id()
687
+ if org_id is not None:
688
+ params["orgId"] = org_id
689
+ if json_body:
690
+ body = json.loads(json_body)
691
+ else:
692
+ body = {}
693
+ if enabled is not None:
694
+ body["enabled"] = enabled
695
+ try:
696
+ result = api.session.rest_put(url, json=body, params=params)
697
+ except WebexError as e:
698
+ handle_rest_error(e)
699
+ typer.echo(f"Updated.")
700
+
701
+
702
+
703
+ @app.command("show-auto-transfer-numbers")
704
+ def show_auto_transfer_numbers(
705
+ workspace_id: str = typer.Argument(help="workspaceId"),
706
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
707
+ debug: bool = typer.Option(False, "--debug"),
708
+ ):
709
+ """Retrieve Transfer Numbers Settings for a Workspace."""
710
+ api = get_api(debug=debug)
711
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/outgoingPermission/autoTransferNumbers"
712
+ params = {}
713
+ org_id = get_org_id()
714
+ if org_id is not None:
715
+ params["orgId"] = org_id
716
+ try:
717
+ result = api.session.rest_get(url, params=params)
718
+ except WebexError as e:
719
+ handle_rest_error(e)
720
+ if output == "json":
721
+ print_json(result)
722
+ else:
723
+ if isinstance(result, dict):
724
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
725
+ elif isinstance(result, list):
726
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
727
+ else:
728
+ print_json(result)
729
+
730
+
731
+
732
+ @app.command("update-auto-transfer-numbers")
733
+ def update_auto_transfer_numbers(
734
+ workspace_id: str = typer.Argument(help="workspaceId"),
735
+ use_custom_transfer_numbers: bool = typer.Option(None, "--use-custom-transfer-numbers/--no-use-custom-transfer-numbers", help="When `true`, use custom settings for the transfer numbers ca"),
736
+ auto_transfer_number1: str = typer.Option(None, "--auto-transfer-number1", help="When calling a specific call type, this workspace will be au"),
737
+ auto_transfer_number2: str = typer.Option(None, "--auto-transfer-number2", help="When calling a specific call type, this workspace will be au"),
738
+ auto_transfer_number3: str = typer.Option(None, "--auto-transfer-number3", help="When calling a specific call type, this workspace will be au"),
739
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
740
+ debug: bool = typer.Option(False, "--debug"),
741
+ ):
742
+ """Modify Transfer Numbers Settings for a Workspace."""
743
+ api = get_api(debug=debug)
744
+ url = f"https://webexapis.com/v1/workspaces/{workspace_id}/features/outgoingPermission/autoTransferNumbers"
745
+ params = {}
746
+ org_id = get_org_id()
747
+ if org_id is not None:
748
+ params["orgId"] = org_id
749
+ if json_body:
750
+ body = json.loads(json_body)
751
+ else:
752
+ body = {}
753
+ if use_custom_transfer_numbers is not None:
754
+ body["useCustomTransferNumbers"] = use_custom_transfer_numbers
755
+ if auto_transfer_number1 is not None:
756
+ body["autoTransferNumber1"] = auto_transfer_number1
757
+ if auto_transfer_number2 is not None:
758
+ body["autoTransferNumber2"] = auto_transfer_number2
759
+ if auto_transfer_number3 is not None:
760
+ body["autoTransferNumber3"] = auto_transfer_number3
761
+ try:
762
+ result = api.session.rest_put(url, json=body, params=params)
763
+ except WebexError as e:
764
+ handle_rest_error(e)
765
+ typer.echo(f"Updated.")
766
+
767
+
768
+
769
+ @app.command("show-music-on-hold")
770
+ def show_music_on_hold(
771
+ workspace_id: str = typer.Argument(help="workspaceId"),
772
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
773
+ debug: bool = typer.Option(False, "--debug"),
774
+ ):
775
+ """Retrieve Music On Hold Settings for a Workspace."""
776
+ api = get_api(debug=debug)
777
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/musicOnHold"
778
+ params = {}
779
+ org_id = get_org_id()
780
+ if org_id is not None:
781
+ params["orgId"] = org_id
782
+ try:
783
+ result = api.session.rest_get(url, params=params)
784
+ except WebexError as e:
785
+ handle_rest_error(e)
786
+ if output == "json":
787
+ print_json(result)
788
+ else:
789
+ if isinstance(result, dict):
790
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
791
+ elif isinstance(result, list):
792
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
793
+ else:
794
+ print_json(result)
795
+
796
+
797
+
798
+ @app.command("update-music-on-hold")
799
+ def update_music_on_hold(
800
+ workspace_id: str = typer.Argument(help="workspaceId"),
801
+ moh_enabled: bool = typer.Option(None, "--moh-enabled/--no-moh-enabled", help="Music on hold is enabled or disabled for the workspace."),
802
+ greeting: str = typer.Option(None, "--greeting", help="Choices: DEFAULT, CUSTOM"),
803
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
804
+ debug: bool = typer.Option(False, "--debug"),
805
+ ):
806
+ """Modify Music On Hold Settings for a Workspace\n\nExample --json-body:\n '{"mohEnabled":true,"greeting":"DEFAULT","audioAnnouncementFile":{"id":"...","fileName":"...","mediaFileType":"WAV","level":"ORGANIZATION"}}'."""
807
+ api = get_api(debug=debug)
808
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/musicOnHold"
809
+ params = {}
810
+ org_id = get_org_id()
811
+ if org_id is not None:
812
+ params["orgId"] = org_id
813
+ if json_body:
814
+ body = json.loads(json_body)
815
+ else:
816
+ body = {}
817
+ if moh_enabled is not None:
818
+ body["mohEnabled"] = moh_enabled
819
+ if greeting is not None:
820
+ body["greeting"] = greeting
821
+ try:
822
+ result = api.session.rest_put(url, json=body, params=params)
823
+ except WebexError as e:
824
+ handle_rest_error(e)
825
+ typer.echo(f"Updated.")
826
+
827
+
828
+
829
+ @app.command("delete-access-codes")
830
+ def delete_access_codes(
831
+ workspace_id: str = typer.Argument(help="workspaceId"),
832
+ access_code: str = typer.Argument(help="accessCode"),
833
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
834
+ debug: bool = typer.Option(False, "--debug"),
835
+ ):
836
+ """Delete a Specific Access Code for a Workspace."""
837
+ if not force:
838
+ typer.confirm(f"Delete {access_code}?", abort=True)
839
+ api = get_api(debug=debug)
840
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/features/accessCodes/{access_code}"
841
+ params = {}
842
+ org_id = get_org_id()
843
+ if org_id is not None:
844
+ params["orgId"] = org_id
845
+ try:
846
+ api.session.rest_delete(url, params=params)
847
+ except WebexError as e:
848
+ handle_rest_error(e)
849
+ typer.echo(f"Deleted: {access_code}")
850
+
851
+
852
+
853
+ @app.command("list-digit-patterns")
854
+ def list_digit_patterns(
855
+ workspace_id: str = typer.Argument(help="workspaceId"),
856
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
857
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
858
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
859
+ debug: bool = typer.Option(False, "--debug"),
860
+ ):
861
+ """Retrieve all Digit Patterns for a Workspace."""
862
+ api = get_api(debug=debug)
863
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/outgoingPermission/digitPatterns"
864
+ params = {}
865
+ if limit > 0:
866
+ params["max"] = limit
867
+ if offset > 0:
868
+ params["start"] = offset
869
+ org_id = get_org_id()
870
+ if org_id is not None:
871
+ params["orgId"] = org_id
872
+ result = None
873
+ try:
874
+ result = api.session.rest_get(url, params=params)
875
+ except WebexError as e:
876
+ handle_rest_error(e)
877
+ result = result or []
878
+ items = result.get("digitPatterns", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
879
+ if output == "json":
880
+ print_json(items)
881
+ else:
882
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
883
+
884
+
885
+
886
+ @app.command("create-digit-patterns")
887
+ def create_digit_patterns(
888
+ workspace_id: str = typer.Argument(help="workspaceId"),
889
+ name: str = typer.Option(None, "--name", help="(required) A unique name for the digit pattern."),
890
+ pattern: str = typer.Option(None, "--pattern", help="(required) The digit pattern to be matched with the input number."),
891
+ action: str = typer.Option(None, "--action", help="(required) Choices: ALLOW, BLOCK, AUTH_CODE, TRANSFER_NUMBER_1, TRANSFER_NUMBER_2, TRANSFER_NUMBER_3"),
892
+ transfer_enabled: bool = typer.Option(None, "--transfer-enabled/--no-transfer-enabled", help="(required) If `true`, allows transfer and forwarding for the call type."),
893
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
894
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
895
+ debug: bool = typer.Option(False, "--debug"),
896
+ ):
897
+ """Create Digit Pattern for a Workspace."""
898
+ api = get_api(debug=debug)
899
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/outgoingPermission/digitPatterns"
900
+ params = {}
901
+ org_id = get_org_id()
902
+ if org_id is not None:
903
+ params["orgId"] = org_id
904
+ if json_body:
905
+ body = json.loads(json_body)
906
+ else:
907
+ body = {}
908
+ if name is not None:
909
+ body["name"] = name
910
+ if pattern is not None:
911
+ body["pattern"] = pattern
912
+ if action is not None:
913
+ body["action"] = action
914
+ if transfer_enabled is not None:
915
+ body["transferEnabled"] = transfer_enabled
916
+ _missing = [f for f in ['name', 'pattern', 'action', 'transferEnabled'] if f not in body or body[f] is None]
917
+ if _missing:
918
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
919
+ raise typer.Exit(1)
920
+ try:
921
+ result = api.session.rest_post(url, json=body, params=params)
922
+ except WebexError as e:
923
+ handle_rest_error(e)
924
+ if output == "json":
925
+ print_json(result)
926
+ elif isinstance(result, dict) and "id" in result:
927
+ typer.echo(f"Created: {result['id']}")
928
+ elif not result or result == {}:
929
+ typer.echo("Created.")
930
+ else:
931
+ print_json(result)
932
+
933
+
934
+
935
+ @app.command("update-digit-patterns-outgoing-permission")
936
+ def update_digit_patterns_outgoing_permission(
937
+ workspace_id: str = typer.Argument(help="workspaceId"),
938
+ use_custom_digit_patterns: bool = typer.Option(None, "--use-custom-digit-patterns/--no-use-custom-digit-patterns", help="When `true`, use custom settings for the digit patterns cate"),
939
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
940
+ debug: bool = typer.Option(False, "--debug"),
941
+ ):
942
+ """Modify the Digit Pattern Category Control Settings for the Workspace."""
943
+ api = get_api(debug=debug)
944
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/outgoingPermission/digitPatterns"
945
+ params = {}
946
+ org_id = get_org_id()
947
+ if org_id is not None:
948
+ params["orgId"] = org_id
949
+ if json_body:
950
+ body = json.loads(json_body)
951
+ else:
952
+ body = {}
953
+ if use_custom_digit_patterns is not None:
954
+ body["useCustomDigitPatterns"] = use_custom_digit_patterns
955
+ try:
956
+ result = api.session.rest_put(url, json=body, params=params)
957
+ except WebexError as e:
958
+ handle_rest_error(e)
959
+ typer.echo(f"Updated.")
960
+
961
+
962
+
963
+ @app.command("delete-digit-patterns-outgoing-permission")
964
+ def delete_digit_patterns_outgoing_permission(
965
+ workspace_id: str = typer.Argument(help="workspaceId"),
966
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
967
+ debug: bool = typer.Option(False, "--debug"),
968
+ ):
969
+ """Delete all Digit Patterns for a Workspace."""
970
+ if not force:
971
+ typer.confirm(f"Delete {workspace_id}?", abort=True)
972
+ api = get_api(debug=debug)
973
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/outgoingPermission/digitPatterns"
974
+ params = {}
975
+ org_id = get_org_id()
976
+ if org_id is not None:
977
+ params["orgId"] = org_id
978
+ try:
979
+ api.session.rest_delete(url, params=params)
980
+ except WebexError as e:
981
+ handle_rest_error(e)
982
+ typer.echo(f"Deleted: {workspace_id}")
983
+
984
+
985
+
986
+ @app.command("show-digit-patterns")
987
+ def show_digit_patterns(
988
+ workspace_id: str = typer.Argument(help="workspaceId"),
989
+ digit_pattern_id: str = typer.Argument(help="digitPatternId"),
990
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
991
+ debug: bool = typer.Option(False, "--debug"),
992
+ ):
993
+ """Retrieve a Digit Pattern details for the Workspace."""
994
+ api = get_api(debug=debug)
995
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/outgoingPermission/digitPatterns/{digit_pattern_id}"
996
+ params = {}
997
+ org_id = get_org_id()
998
+ if org_id is not None:
999
+ params["orgId"] = org_id
1000
+ try:
1001
+ result = api.session.rest_get(url, params=params)
1002
+ except WebexError as e:
1003
+ handle_rest_error(e)
1004
+ if output == "json":
1005
+ print_json(result)
1006
+ else:
1007
+ if isinstance(result, dict):
1008
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1009
+ elif isinstance(result, list):
1010
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1011
+ else:
1012
+ print_json(result)
1013
+
1014
+
1015
+
1016
+ @app.command("update-digit-patterns-outgoing-permission-1")
1017
+ def update_digit_patterns_outgoing_permission_1(
1018
+ workspace_id: str = typer.Argument(help="workspaceId"),
1019
+ digit_pattern_id: str = typer.Argument(help="digitPatternId"),
1020
+ name: str = typer.Option(None, "--name", help="A unique name for the digit pattern."),
1021
+ pattern: str = typer.Option(None, "--pattern", help="The digit pattern to be matched with the input number."),
1022
+ action: str = typer.Option(None, "--action", help="Choices: ALLOW, BLOCK, AUTH_CODE, TRANSFER_NUMBER_1, TRANSFER_NUMBER_2, TRANSFER_NUMBER_3"),
1023
+ transfer_enabled: bool = typer.Option(None, "--transfer-enabled/--no-transfer-enabled", help="If `true`, allows transfer and forwarding for the call type."),
1024
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1025
+ debug: bool = typer.Option(False, "--debug"),
1026
+ ):
1027
+ """Modify a Digit Pattern for the Workspace."""
1028
+ api = get_api(debug=debug)
1029
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/outgoingPermission/digitPatterns/{digit_pattern_id}"
1030
+ params = {}
1031
+ org_id = get_org_id()
1032
+ if org_id is not None:
1033
+ params["orgId"] = org_id
1034
+ if json_body:
1035
+ body = json.loads(json_body)
1036
+ else:
1037
+ body = {}
1038
+ if name is not None:
1039
+ body["name"] = name
1040
+ if pattern is not None:
1041
+ body["pattern"] = pattern
1042
+ if action is not None:
1043
+ body["action"] = action
1044
+ if transfer_enabled is not None:
1045
+ body["transferEnabled"] = transfer_enabled
1046
+ try:
1047
+ result = api.session.rest_put(url, json=body, params=params)
1048
+ except WebexError as e:
1049
+ handle_rest_error(e)
1050
+ typer.echo(f"Updated.")
1051
+
1052
+
1053
+
1054
+ @app.command("delete-digit-patterns-outgoing-permission-1")
1055
+ def delete_digit_patterns_outgoing_permission_1(
1056
+ workspace_id: str = typer.Argument(help="workspaceId"),
1057
+ digit_pattern_id: str = typer.Argument(help="digitPatternId"),
1058
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1059
+ debug: bool = typer.Option(False, "--debug"),
1060
+ ):
1061
+ """Delete a Digit Pattern for the Workspace."""
1062
+ if not force:
1063
+ typer.confirm(f"Delete {digit_pattern_id}?", abort=True)
1064
+ api = get_api(debug=debug)
1065
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/outgoingPermission/digitPatterns/{digit_pattern_id}"
1066
+ params = {}
1067
+ org_id = get_org_id()
1068
+ if org_id is not None:
1069
+ params["orgId"] = org_id
1070
+ try:
1071
+ api.session.rest_delete(url, params=params)
1072
+ except WebexError as e:
1073
+ handle_rest_error(e)
1074
+ typer.echo(f"Deleted: {digit_pattern_id}")
1075
+
1076
+
1077
+
1078
+ @app.command("upload-call-intercept")
1079
+ def upload_call_intercept(
1080
+ workspace_id: str = typer.Argument(help="workspaceId"),
1081
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1082
+ debug: bool = typer.Option(False, "--debug"),
1083
+ ):
1084
+ """Upload Call Intercept Announcement file for a Workspace."""
1085
+ api = get_api(debug=debug)
1086
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/features/intercept/actions/announcementUpload/invoke"
1087
+ params = {}
1088
+ org_id = get_org_id()
1089
+ if org_id is not None:
1090
+ params["orgId"] = org_id
1091
+ if json_body:
1092
+ body = json.loads(json_body)
1093
+ else:
1094
+ body = {}
1095
+ try:
1096
+ result = api.session.rest_post(url, json=body, params=params)
1097
+ except WebexError as e:
1098
+ handle_rest_error(e)
1099
+ print_json(result)
1100
+
1101
+
1102
+
1103
+ @app.command("show-call-recordings")
1104
+ def show_call_recordings(
1105
+ workspace_id: str = typer.Argument(help="workspaceId"),
1106
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1107
+ debug: bool = typer.Option(False, "--debug"),
1108
+ ):
1109
+ """Retrieve Call Recording Settings for a Workspace."""
1110
+ api = get_api(debug=debug)
1111
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/features/callRecordings"
1112
+ params = {}
1113
+ org_id = get_org_id()
1114
+ if org_id is not None:
1115
+ params["orgId"] = org_id
1116
+ try:
1117
+ result = api.session.rest_get(url, params=params)
1118
+ except WebexError as e:
1119
+ handle_rest_error(e)
1120
+ if output == "json":
1121
+ print_json(result)
1122
+ else:
1123
+ if isinstance(result, dict):
1124
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1125
+ elif isinstance(result, list):
1126
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1127
+ else:
1128
+ print_json(result)
1129
+
1130
+
1131
+
1132
+ @app.command("update-call-recordings")
1133
+ def update_call_recordings(
1134
+ workspace_id: str = typer.Argument(help="workspaceId"),
1135
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if call recording is enabled."),
1136
+ record: str = typer.Option(None, "--record", help="Choices: Always, Never, Always with Pause/Resume, On Demand with User Initiated Start"),
1137
+ record_voicemail_enabled: bool = typer.Option(None, "--record-voicemail-enabled/--no-record-voicemail-enabled", help="When `true`, voicemail messages are also recorded."),
1138
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1139
+ debug: bool = typer.Option(False, "--debug"),
1140
+ ):
1141
+ """Modify Call Recording Settings for a Workspace\n\nExample --json-body:\n '{"enabled":true,"record":"Always","recordVoicemailEnabled":true,"notification":{"type":"Beep","enabled":true},"repeat":{"interval":0,"enabled":true},"startStopAnnouncement":{"internalCallsEnabled":true,"pstnCallsEnabled":true}}'."""
1142
+ api = get_api(debug=debug)
1143
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/features/callRecordings"
1144
+ params = {}
1145
+ org_id = get_org_id()
1146
+ if org_id is not None:
1147
+ params["orgId"] = org_id
1148
+ if json_body:
1149
+ body = json.loads(json_body)
1150
+ else:
1151
+ body = {}
1152
+ if enabled is not None:
1153
+ body["enabled"] = enabled
1154
+ if record is not None:
1155
+ body["record"] = record
1156
+ if record_voicemail_enabled is not None:
1157
+ body["recordVoicemailEnabled"] = record_voicemail_enabled
1158
+ try:
1159
+ result = api.session.rest_put(url, json=body, params=params)
1160
+ except WebexError as e:
1161
+ handle_rest_error(e)
1162
+ typer.echo(f"Updated.")
1163
+
1164
+
1165
+
1166
+ @app.command("list-available-numbers-workspaces")
1167
+ def list_available_numbers_workspaces(
1168
+ location_id: str = typer.Option(None, "--location-id", help="Return the list of phone numbers for this location within th"),
1169
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1170
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1171
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1172
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1173
+ debug: bool = typer.Option(False, "--debug"),
1174
+ ):
1175
+ """Get Workspace Available Phone Numbers."""
1176
+ api = get_api(debug=debug)
1177
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/availableNumbers"
1178
+ params = {}
1179
+ if location_id is not None:
1180
+ params["locationId"] = location_id
1181
+ if phone_number is not None:
1182
+ params["phoneNumber"] = phone_number
1183
+ if limit > 0:
1184
+ params["max"] = limit
1185
+ if offset > 0:
1186
+ params["start"] = offset
1187
+ org_id = get_org_id()
1188
+ if org_id is not None:
1189
+ params["orgId"] = org_id
1190
+ result = None
1191
+ try:
1192
+ result = api.session.rest_get(url, params=params)
1193
+ except WebexError as e:
1194
+ handle_rest_error(e)
1195
+ result = result or []
1196
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1197
+ if output == "json":
1198
+ print_json(items)
1199
+ else:
1200
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1201
+
1202
+
1203
+
1204
+ @app.command("list-available-numbers-emergency-callback-number")
1205
+ def list_available_numbers_emergency_callback_number(
1206
+ workspace_id: str = typer.Argument(help="workspaceId"),
1207
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1208
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
1209
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1210
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1211
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1212
+ debug: bool = typer.Option(False, "--debug"),
1213
+ ):
1214
+ """Get Workspace ECBN Available Phone Numbers."""
1215
+ api = get_api(debug=debug)
1216
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/emergencyCallbackNumber/availableNumbers"
1217
+ params = {}
1218
+ if phone_number is not None:
1219
+ params["phoneNumber"] = phone_number
1220
+ if owner_name is not None:
1221
+ params["ownerName"] = owner_name
1222
+ if limit > 0:
1223
+ params["max"] = limit
1224
+ if offset > 0:
1225
+ params["start"] = offset
1226
+ org_id = get_org_id()
1227
+ if org_id is not None:
1228
+ params["orgId"] = org_id
1229
+ result = None
1230
+ try:
1231
+ result = api.session.rest_get(url, params=params)
1232
+ except WebexError as e:
1233
+ handle_rest_error(e)
1234
+ result = result or []
1235
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1236
+ if output == "json":
1237
+ print_json(items)
1238
+ else:
1239
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1240
+
1241
+
1242
+
1243
+ @app.command("list-available-numbers-call-forwarding")
1244
+ def list_available_numbers_call_forwarding(
1245
+ workspace_id: str = typer.Argument(help="workspaceId"),
1246
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1247
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
1248
+ extension: str = typer.Option(None, "--extension", help="Returns the list of PSTN phone numbers with the given `exten"),
1249
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1250
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1251
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1252
+ debug: bool = typer.Option(False, "--debug"),
1253
+ ):
1254
+ """Get Workspace Call Forward Available Phone Numbers."""
1255
+ api = get_api(debug=debug)
1256
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/callForwarding/availableNumbers"
1257
+ params = {}
1258
+ if phone_number is not None:
1259
+ params["phoneNumber"] = phone_number
1260
+ if owner_name is not None:
1261
+ params["ownerName"] = owner_name
1262
+ if extension is not None:
1263
+ params["extension"] = extension
1264
+ if limit > 0:
1265
+ params["max"] = limit
1266
+ if offset > 0:
1267
+ params["start"] = offset
1268
+ org_id = get_org_id()
1269
+ if org_id is not None:
1270
+ params["orgId"] = org_id
1271
+ result = None
1272
+ try:
1273
+ result = api.session.rest_get(url, params=params)
1274
+ except WebexError as e:
1275
+ handle_rest_error(e)
1276
+ result = result or []
1277
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1278
+ if output == "json":
1279
+ print_json(items)
1280
+ else:
1281
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1282
+
1283
+
1284
+
1285
+ @app.command("list-available-numbers-call-intercept")
1286
+ def list_available_numbers_call_intercept(
1287
+ workspace_id: str = typer.Argument(help="workspaceId"),
1288
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
1289
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
1290
+ extension: str = typer.Option(None, "--extension", help="Returns the list of PSTN phone numbers with the given `exten"),
1291
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1292
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1293
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1294
+ debug: bool = typer.Option(False, "--debug"),
1295
+ ):
1296
+ """Get Workspace Call Intercept Available Phone Numbers."""
1297
+ api = get_api(debug=debug)
1298
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/callIntercept/availableNumbers"
1299
+ params = {}
1300
+ if phone_number is not None:
1301
+ params["phoneNumber"] = phone_number
1302
+ if owner_name is not None:
1303
+ params["ownerName"] = owner_name
1304
+ if extension is not None:
1305
+ params["extension"] = extension
1306
+ if limit > 0:
1307
+ params["max"] = limit
1308
+ if offset > 0:
1309
+ params["start"] = offset
1310
+ org_id = get_org_id()
1311
+ if org_id is not None:
1312
+ params["orgId"] = org_id
1313
+ result = None
1314
+ try:
1315
+ result = api.session.rest_get(url, params=params)
1316
+ except WebexError as e:
1317
+ handle_rest_error(e)
1318
+ result = result or []
1319
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1320
+ if output == "json":
1321
+ print_json(items)
1322
+ else:
1323
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1324
+
1325
+
1326
+
1327
+ @app.command("show-anonymous-call-reject")
1328
+ def show_anonymous_call_reject(
1329
+ workspace_id: str = typer.Argument(help="workspaceId"),
1330
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1331
+ debug: bool = typer.Option(False, "--debug"),
1332
+ ):
1333
+ """Retrieve Anonymous Call Settings for a Workspace."""
1334
+ api = get_api(debug=debug)
1335
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/anonymousCallReject"
1336
+ params = {}
1337
+ org_id = get_org_id()
1338
+ if org_id is not None:
1339
+ params["orgId"] = org_id
1340
+ try:
1341
+ result = api.session.rest_get(url, params=params)
1342
+ except WebexError as e:
1343
+ handle_rest_error(e)
1344
+ if output == "json":
1345
+ print_json(result)
1346
+ else:
1347
+ if isinstance(result, dict):
1348
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1349
+ elif isinstance(result, list):
1350
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1351
+ else:
1352
+ print_json(result)
1353
+
1354
+
1355
+
1356
+ @app.command("update-anonymous-call-reject")
1357
+ def update_anonymous_call_reject(
1358
+ workspace_id: str = typer.Argument(help="workspaceId"),
1359
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if the Anonymous Call Rejection feature is enabled."),
1360
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1361
+ debug: bool = typer.Option(False, "--debug"),
1362
+ ):
1363
+ """Modify Anonymous Call Settings for a Workspace."""
1364
+ api = get_api(debug=debug)
1365
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/anonymousCallReject"
1366
+ params = {}
1367
+ org_id = get_org_id()
1368
+ if org_id is not None:
1369
+ params["orgId"] = org_id
1370
+ if json_body:
1371
+ body = json.loads(json_body)
1372
+ else:
1373
+ body = {}
1374
+ if enabled is not None:
1375
+ body["enabled"] = enabled
1376
+ try:
1377
+ result = api.session.rest_put(url, json=body, params=params)
1378
+ except WebexError as e:
1379
+ handle_rest_error(e)
1380
+ typer.echo(f"Updated.")
1381
+
1382
+
1383
+
1384
+ @app.command("show-barge-in")
1385
+ def show_barge_in(
1386
+ workspace_id: str = typer.Argument(help="workspaceId"),
1387
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1388
+ debug: bool = typer.Option(False, "--debug"),
1389
+ ):
1390
+ """Retrieve Barge In Call Settings for a Workspace."""
1391
+ api = get_api(debug=debug)
1392
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/bargeIn"
1393
+ params = {}
1394
+ org_id = get_org_id()
1395
+ if org_id is not None:
1396
+ params["orgId"] = org_id
1397
+ try:
1398
+ result = api.session.rest_get(url, params=params)
1399
+ except WebexError as e:
1400
+ handle_rest_error(e)
1401
+ if output == "json":
1402
+ print_json(result)
1403
+ else:
1404
+ if isinstance(result, dict):
1405
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1406
+ elif isinstance(result, list):
1407
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1408
+ else:
1409
+ print_json(result)
1410
+
1411
+
1412
+
1413
+ @app.command("update-barge-in")
1414
+ def update_barge_in(
1415
+ workspace_id: str = typer.Argument(help="workspaceId"),
1416
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if the Barge In feature is enabled."),
1417
+ tone_enabled: bool = typer.Option(None, "--tone-enabled/--no-tone-enabled", help="When `true`, a tone is played when someone barges into a cal"),
1418
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1419
+ debug: bool = typer.Option(False, "--debug"),
1420
+ ):
1421
+ """Modify Barge In Call Settings for a Workspace."""
1422
+ api = get_api(debug=debug)
1423
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/bargeIn"
1424
+ params = {}
1425
+ org_id = get_org_id()
1426
+ if org_id is not None:
1427
+ params["orgId"] = org_id
1428
+ if json_body:
1429
+ body = json.loads(json_body)
1430
+ else:
1431
+ body = {}
1432
+ if enabled is not None:
1433
+ body["enabled"] = enabled
1434
+ if tone_enabled is not None:
1435
+ body["toneEnabled"] = tone_enabled
1436
+ try:
1437
+ result = api.session.rest_put(url, json=body, params=params)
1438
+ except WebexError as e:
1439
+ handle_rest_error(e)
1440
+ typer.echo(f"Updated.")
1441
+
1442
+
1443
+
1444
+ @app.command("show-do-not-disturb")
1445
+ def show_do_not_disturb(
1446
+ workspace_id: str = typer.Argument(help="workspaceId"),
1447
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1448
+ debug: bool = typer.Option(False, "--debug"),
1449
+ ):
1450
+ """Retrieve DoNotDisturb Settings for a Workspace."""
1451
+ api = get_api(debug=debug)
1452
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/doNotDisturb"
1453
+ params = {}
1454
+ org_id = get_org_id()
1455
+ if org_id is not None:
1456
+ params["orgId"] = org_id
1457
+ try:
1458
+ result = api.session.rest_get(url, params=params)
1459
+ except WebexError as e:
1460
+ handle_rest_error(e)
1461
+ if output == "json":
1462
+ print_json(result)
1463
+ else:
1464
+ if isinstance(result, dict):
1465
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1466
+ elif isinstance(result, list):
1467
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1468
+ else:
1469
+ print_json(result)
1470
+
1471
+
1472
+
1473
+ @app.command("update-do-not-disturb")
1474
+ def update_do_not_disturb(
1475
+ workspace_id: str = typer.Argument(help="workspaceId"),
1476
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if the DoNotDisturb feature is enabled."),
1477
+ ring_splash_enabled: bool = typer.Option(None, "--ring-splash-enabled/--no-ring-splash-enabled", help="When `true`, enables ring reminder when you receive an incom"),
1478
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1479
+ debug: bool = typer.Option(False, "--debug"),
1480
+ ):
1481
+ """Modify DoNotDisturb Settings for a Workspace."""
1482
+ api = get_api(debug=debug)
1483
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/doNotDisturb"
1484
+ params = {}
1485
+ org_id = get_org_id()
1486
+ if org_id is not None:
1487
+ params["orgId"] = org_id
1488
+ if json_body:
1489
+ body = json.loads(json_body)
1490
+ else:
1491
+ body = {}
1492
+ if enabled is not None:
1493
+ body["enabled"] = enabled
1494
+ if ring_splash_enabled is not None:
1495
+ body["ringSplashEnabled"] = ring_splash_enabled
1496
+ try:
1497
+ result = api.session.rest_put(url, json=body, params=params)
1498
+ except WebexError as e:
1499
+ handle_rest_error(e)
1500
+ typer.echo(f"Updated.")
1501
+
1502
+
1503
+
1504
+ @app.command("show-call-bridge")
1505
+ def show_call_bridge(
1506
+ workspace_id: str = typer.Argument(help="workspaceId"),
1507
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1508
+ debug: bool = typer.Option(False, "--debug"),
1509
+ ):
1510
+ """Retrieve Call Bridge Warning Tone Settings for a Workspace."""
1511
+ api = get_api(debug=debug)
1512
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/callBridge"
1513
+ params = {}
1514
+ org_id = get_org_id()
1515
+ if org_id is not None:
1516
+ params["orgId"] = org_id
1517
+ try:
1518
+ result = api.session.rest_get(url, params=params)
1519
+ except WebexError as e:
1520
+ handle_rest_error(e)
1521
+ if output == "json":
1522
+ print_json(result)
1523
+ else:
1524
+ if isinstance(result, dict):
1525
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1526
+ elif isinstance(result, list):
1527
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1528
+ else:
1529
+ print_json(result)
1530
+
1531
+
1532
+
1533
+ @app.command("update-call-bridge")
1534
+ def update_call_bridge(
1535
+ workspace_id: str = typer.Argument(help="workspaceId"),
1536
+ warning_tone_enabled: bool = typer.Option(None, "--warning-tone-enabled/--no-warning-tone-enabled", help="`true` if the Call Bridge Warning Tone feature is enabled."),
1537
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1538
+ debug: bool = typer.Option(False, "--debug"),
1539
+ ):
1540
+ """Modify Call Bridge Warning Tone Settings for a Workspace."""
1541
+ api = get_api(debug=debug)
1542
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/callBridge"
1543
+ params = {}
1544
+ org_id = get_org_id()
1545
+ if org_id is not None:
1546
+ params["orgId"] = org_id
1547
+ if json_body:
1548
+ body = json.loads(json_body)
1549
+ else:
1550
+ body = {}
1551
+ if warning_tone_enabled is not None:
1552
+ body["warningToneEnabled"] = warning_tone_enabled
1553
+ try:
1554
+ result = api.session.rest_put(url, json=body, params=params)
1555
+ except WebexError as e:
1556
+ handle_rest_error(e)
1557
+ typer.echo(f"Updated.")
1558
+
1559
+
1560
+
1561
+ @app.command("list-push-to-talk")
1562
+ def list_push_to_talk(
1563
+ workspace_id: str = typer.Argument(help="workspaceId"),
1564
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1565
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1566
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1567
+ debug: bool = typer.Option(False, "--debug"),
1568
+ ):
1569
+ """Read Push-to-Talk Settings for a Workspace."""
1570
+ api = get_api(debug=debug)
1571
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/pushToTalk"
1572
+ params = {}
1573
+ if limit > 0:
1574
+ params["max"] = limit
1575
+ if offset > 0:
1576
+ params["start"] = offset
1577
+ org_id = get_org_id()
1578
+ if org_id is not None:
1579
+ params["orgId"] = org_id
1580
+ result = None
1581
+ try:
1582
+ result = api.session.rest_get(url, params=params)
1583
+ except WebexError as e:
1584
+ handle_rest_error(e)
1585
+ result = result or []
1586
+ items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1587
+ if output == "json":
1588
+ print_json(items)
1589
+ else:
1590
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1591
+
1592
+
1593
+
1594
+ @app.command("update-push-to-talk")
1595
+ def update_push_to_talk(
1596
+ workspace_id: str = typer.Argument(help="workspaceId"),
1597
+ allow_auto_answer: bool = typer.Option(None, "--allow-auto-answer/--no-allow-auto-answer", help="`true` if Push-to-Talk feature is enabled."),
1598
+ connection_type: str = typer.Option(None, "--connection-type", help="Choices: ONE_WAY, TWO_WAY"),
1599
+ access_type: str = typer.Option(None, "--access-type", help="Choices: ALLOW_MEMBERS, BLOCK_MEMBERS"),
1600
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1601
+ debug: bool = typer.Option(False, "--debug"),
1602
+ ):
1603
+ """Configure Push-to-Talk Settings for a Workspace\n\nExample --json-body:\n '{"allowAutoAnswer":true,"connectionType":"ONE_WAY","accessType":"ALLOW_MEMBERS","members":["..."]}'."""
1604
+ api = get_api(debug=debug)
1605
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/pushToTalk"
1606
+ params = {}
1607
+ org_id = get_org_id()
1608
+ if org_id is not None:
1609
+ params["orgId"] = org_id
1610
+ if json_body:
1611
+ body = json.loads(json_body)
1612
+ else:
1613
+ body = {}
1614
+ if allow_auto_answer is not None:
1615
+ body["allowAutoAnswer"] = allow_auto_answer
1616
+ if connection_type is not None:
1617
+ body["connectionType"] = connection_type
1618
+ if access_type is not None:
1619
+ body["accessType"] = access_type
1620
+ try:
1621
+ result = api.session.rest_put(url, json=body, params=params)
1622
+ except WebexError as e:
1623
+ handle_rest_error(e)
1624
+ typer.echo(f"Updated.")
1625
+
1626
+
1627
+
1628
+ @app.command("list-privacy")
1629
+ def list_privacy(
1630
+ workspace_id: str = typer.Argument(help="workspaceId"),
1631
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1632
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1633
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1634
+ debug: bool = typer.Option(False, "--debug"),
1635
+ ):
1636
+ """Retrieve Privacy Settings for a Workspace."""
1637
+ api = get_api(debug=debug)
1638
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/privacy"
1639
+ params = {}
1640
+ if limit > 0:
1641
+ params["max"] = limit
1642
+ if offset > 0:
1643
+ params["start"] = offset
1644
+ org_id = get_org_id()
1645
+ if org_id is not None:
1646
+ params["orgId"] = org_id
1647
+ result = None
1648
+ try:
1649
+ result = api.session.rest_get(url, params=params)
1650
+ except WebexError as e:
1651
+ handle_rest_error(e)
1652
+ result = result or []
1653
+ items = result.get("monitoringAgents", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1654
+ if output == "json":
1655
+ print_json(items)
1656
+ else:
1657
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1658
+
1659
+
1660
+
1661
+ @app.command("update-privacy")
1662
+ def update_privacy(
1663
+ workspace_id: str = typer.Argument(help="workspaceId"),
1664
+ aa_extension_dialing_enabled: bool = typer.Option(None, "--aa-extension-dialing-enabled/--no-aa-extension-dialing-enabled", help="When `true` auto attendant extension dialing is enabled."),
1665
+ aa_naming_dialing_enabled: bool = typer.Option(None, "--aa-naming-dialing-enabled/--no-aa-naming-dialing-enabled", help="When `true` auto attendant dialing by first or last name is"),
1666
+ enable_phone_status_directory_privacy: bool = typer.Option(None, "--enable-phone-status-directory-privacy/--no-enable-phone-status-directory-privacy", help="When `true` phone status directory privacy is enabled."),
1667
+ enable_phone_status_pickup_barge_in_privacy: bool = typer.Option(None, "--enable-phone-status-pickup-barge-in-privacy/--no-enable-phone-status-pickup-barge-in-privacy", help="When `true` privacy is enforced for call pickup and barge-in"),
1668
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1669
+ debug: bool = typer.Option(False, "--debug"),
1670
+ ):
1671
+ """Modify Privacy Settings for a Workspace\n\nExample --json-body:\n '{"aaExtensionDialingEnabled":true,"aaNamingDialingEnabled":true,"enablePhoneStatusDirectoryPrivacy":true,"enablePhoneStatusPickupBargeInPrivacy":true,"monitoringAgents":["..."]}'."""
1672
+ api = get_api(debug=debug)
1673
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/privacy"
1674
+ params = {}
1675
+ org_id = get_org_id()
1676
+ if org_id is not None:
1677
+ params["orgId"] = org_id
1678
+ if json_body:
1679
+ body = json.loads(json_body)
1680
+ else:
1681
+ body = {}
1682
+ if aa_extension_dialing_enabled is not None:
1683
+ body["aaExtensionDialingEnabled"] = aa_extension_dialing_enabled
1684
+ if aa_naming_dialing_enabled is not None:
1685
+ body["aaNamingDialingEnabled"] = aa_naming_dialing_enabled
1686
+ if enable_phone_status_directory_privacy is not None:
1687
+ body["enablePhoneStatusDirectoryPrivacy"] = enable_phone_status_directory_privacy
1688
+ if enable_phone_status_pickup_barge_in_privacy is not None:
1689
+ body["enablePhoneStatusPickupBargeInPrivacy"] = enable_phone_status_pickup_barge_in_privacy
1690
+ try:
1691
+ result = api.session.rest_put(url, json=body, params=params)
1692
+ except WebexError as e:
1693
+ handle_rest_error(e)
1694
+ typer.echo(f"Updated.")
1695
+
1696
+
1697
+
1698
+ @app.command("show-voicemail")
1699
+ def show_voicemail(
1700
+ workspace_id: str = typer.Argument(help="workspaceId"),
1701
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1702
+ debug: bool = typer.Option(False, "--debug"),
1703
+ ):
1704
+ """Read Voicemail Settings for a Workspace."""
1705
+ api = get_api(debug=debug)
1706
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/voicemail"
1707
+ params = {}
1708
+ org_id = get_org_id()
1709
+ if org_id is not None:
1710
+ params["orgId"] = org_id
1711
+ try:
1712
+ result = api.session.rest_get(url, params=params)
1713
+ except WebexError as e:
1714
+ handle_rest_error(e)
1715
+ if output == "json":
1716
+ print_json(result)
1717
+ else:
1718
+ if isinstance(result, dict):
1719
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1720
+ elif isinstance(result, list):
1721
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1722
+ else:
1723
+ print_json(result)
1724
+
1725
+
1726
+
1727
+ @app.command("update-voicemail")
1728
+ def update_voicemail(
1729
+ workspace_id: str = typer.Argument(help="workspaceId"),
1730
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Voicemail is enabled or disabled."),
1731
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1732
+ debug: bool = typer.Option(False, "--debug"),
1733
+ ):
1734
+ """Configure Voicemail Settings for a Workspace\n\nExample --json-body:\n '{"notifications":{"enabled":true,"destination":"...","smsDestination":"..."},"transferToNumber":{"enabled":true,"destination":"..."},"enabled":true,"sendAllCalls":{"enabled":true},"sendBusyCalls":{"enabled":true,"greeting":"DEFAULT"},"sendUnansweredCalls":{"enabled":true,"greeting":"DEFAULT","numberOfRings":0},"emailCopyOfMessage":{"enabled":true,"emailId":"..."},"messageStorage":{"mwiEnabled":true,"storageType":"INTERNAL","externalEmail":"..."}}'."""
1735
+ api = get_api(debug=debug)
1736
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/voicemail"
1737
+ params = {}
1738
+ org_id = get_org_id()
1739
+ if org_id is not None:
1740
+ params["orgId"] = org_id
1741
+ if json_body:
1742
+ body = json.loads(json_body)
1743
+ else:
1744
+ body = {}
1745
+ if enabled is not None:
1746
+ body["enabled"] = enabled
1747
+ try:
1748
+ result = api.session.rest_put(url, json=body, params=params)
1749
+ except WebexError as e:
1750
+ handle_rest_error(e)
1751
+ typer.echo(f"Updated.")
1752
+
1753
+
1754
+
1755
+ @app.command("update-passcode")
1756
+ def update_passcode(
1757
+ place_id: str = typer.Argument(help="placeId"),
1758
+ passcode: str = typer.Option(None, "--passcode", help="Voicemail access passcode. The minimum length of the passcod"),
1759
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1760
+ debug: bool = typer.Option(False, "--debug"),
1761
+ ):
1762
+ """Modify Voicemail Passcode for a Workspace."""
1763
+ api = get_api(debug=debug)
1764
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{place_id}/voicemail/passcode"
1765
+ params = {}
1766
+ org_id = get_org_id()
1767
+ if org_id is not None:
1768
+ params["orgId"] = org_id
1769
+ if json_body:
1770
+ body = json.loads(json_body)
1771
+ else:
1772
+ body = {}
1773
+ if passcode is not None:
1774
+ body["passcode"] = passcode
1775
+ try:
1776
+ result = api.session.rest_put(url, json=body, params=params)
1777
+ except WebexError as e:
1778
+ handle_rest_error(e)
1779
+ typer.echo(f"Updated.")
1780
+
1781
+
1782
+
1783
+ @app.command("show-criteria-sequential-ring")
1784
+ def show_criteria_sequential_ring(
1785
+ workspace_id: str = typer.Argument(help="workspaceId"),
1786
+ id: str = typer.Argument(help="id"),
1787
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1788
+ debug: bool = typer.Option(False, "--debug"),
1789
+ ):
1790
+ """Retrieve Sequential Ring Criteria for a Workspace."""
1791
+ api = get_api(debug=debug)
1792
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/sequentialRing/criteria/{id}"
1793
+ params = {}
1794
+ org_id = get_org_id()
1795
+ if org_id is not None:
1796
+ params["orgId"] = org_id
1797
+ try:
1798
+ result = api.session.rest_get(url, params=params)
1799
+ except WebexError as e:
1800
+ handle_rest_error(e)
1801
+ if output == "json":
1802
+ print_json(result)
1803
+ else:
1804
+ if isinstance(result, dict):
1805
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1806
+ elif isinstance(result, list):
1807
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1808
+ else:
1809
+ print_json(result)
1810
+
1811
+
1812
+
1813
+ @app.command("update-criteria-sequential-ring")
1814
+ def update_criteria_sequential_ring(
1815
+ workspace_id: str = typer.Argument(help="workspaceId"),
1816
+ id: str = typer.Argument(help="id"),
1817
+ schedule_name: str = typer.Option(None, "--schedule-name", help="Name of the location's schedule which determines when the se"),
1818
+ schedule_type: str = typer.Option(None, "--schedule-type", help="Choices: holidays, businessHours"),
1819
+ schedule_level: str = typer.Option(None, "--schedule-level", help="Choices: GROUP"),
1820
+ calls_from: str = typer.Option(None, "--calls-from", help="Choices: SELECT_PHONE_NUMBERS, ANY_PHONE_NUMBER"),
1821
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true` incoming calls from private numbers are allowed."),
1822
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true` incoming calls from unavailable numbers are allo"),
1823
+ ring_enabled: bool = typer.Option(None, "--ring-enabled/--no-ring-enabled", help="When set to `true` sequential ringing is enabled for calls t"),
1824
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1825
+ debug: bool = typer.Option(False, "--debug"),
1826
+ ):
1827
+ """Modify Sequential Ring Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"holidays","scheduleLevel":"GROUP","callsFrom":"SELECT_PHONE_NUMBERS","anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."],"ringEnabled":true}'."""
1828
+ api = get_api(debug=debug)
1829
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/sequentialRing/criteria/{id}"
1830
+ params = {}
1831
+ org_id = get_org_id()
1832
+ if org_id is not None:
1833
+ params["orgId"] = org_id
1834
+ if json_body:
1835
+ body = json.loads(json_body)
1836
+ else:
1837
+ body = {}
1838
+ if schedule_name is not None:
1839
+ body["scheduleName"] = schedule_name
1840
+ if schedule_type is not None:
1841
+ body["scheduleType"] = schedule_type
1842
+ if schedule_level is not None:
1843
+ body["scheduleLevel"] = schedule_level
1844
+ if calls_from is not None:
1845
+ body["callsFrom"] = calls_from
1846
+ if anonymous_callers_enabled is not None:
1847
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
1848
+ if unavailable_callers_enabled is not None:
1849
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
1850
+ if ring_enabled is not None:
1851
+ body["ringEnabled"] = ring_enabled
1852
+ try:
1853
+ result = api.session.rest_put(url, json=body, params=params)
1854
+ except WebexError as e:
1855
+ handle_rest_error(e)
1856
+ typer.echo(f"Updated.")
1857
+
1858
+
1859
+
1860
+ @app.command("delete-criteria-sequential-ring")
1861
+ def delete_criteria_sequential_ring(
1862
+ workspace_id: str = typer.Argument(help="workspaceId"),
1863
+ id: str = typer.Argument(help="id"),
1864
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
1865
+ debug: bool = typer.Option(False, "--debug"),
1866
+ ):
1867
+ """Delete Sequential Ring Criteria for a Workspace."""
1868
+ if not force:
1869
+ typer.confirm(f"Delete {id}?", abort=True)
1870
+ api = get_api(debug=debug)
1871
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/sequentialRing/criteria/{id}"
1872
+ params = {}
1873
+ org_id = get_org_id()
1874
+ if org_id is not None:
1875
+ params["orgId"] = org_id
1876
+ try:
1877
+ api.session.rest_delete(url, params=params)
1878
+ except WebexError as e:
1879
+ handle_rest_error(e)
1880
+ typer.echo(f"Deleted: {id}")
1881
+
1882
+
1883
+
1884
+ @app.command("show-call-policies")
1885
+ def show_call_policies(
1886
+ workspace_id: str = typer.Argument(help="workspaceId"),
1887
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1888
+ debug: bool = typer.Option(False, "--debug"),
1889
+ ):
1890
+ """Read Call Policy Settings for a Workspace."""
1891
+ api = get_api(debug=debug)
1892
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/callPolicies"
1893
+ params = {}
1894
+ org_id = get_org_id()
1895
+ if org_id is not None:
1896
+ params["orgId"] = org_id
1897
+ try:
1898
+ result = api.session.rest_get(url, params=params)
1899
+ except WebexError as e:
1900
+ handle_rest_error(e)
1901
+ if output == "json":
1902
+ print_json(result)
1903
+ else:
1904
+ if isinstance(result, dict):
1905
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1906
+ elif isinstance(result, list):
1907
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1908
+ else:
1909
+ print_json(result)
1910
+
1911
+
1912
+
1913
+ @app.command("update-call-policies")
1914
+ def update_call_policies(
1915
+ workspace_id: str = typer.Argument(help="workspaceId"),
1916
+ connected_line_id_privacy_on_redirected_calls: str = typer.Option(None, "--connected-line-id-privacy-on-redirected-calls", help="Choices: NO_PRIVACY, PRIVACY_FOR_EXTERNAL_CALLS, PRIVACY_FOR_ALL_CALLS"),
1917
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1918
+ debug: bool = typer.Option(False, "--debug"),
1919
+ ):
1920
+ """Configure Call Policy Settings for a Workspace."""
1921
+ api = get_api(debug=debug)
1922
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/callPolicies"
1923
+ params = {}
1924
+ org_id = get_org_id()
1925
+ if org_id is not None:
1926
+ params["orgId"] = org_id
1927
+ if json_body:
1928
+ body = json.loads(json_body)
1929
+ else:
1930
+ body = {}
1931
+ if connected_line_id_privacy_on_redirected_calls is not None:
1932
+ body["connectedLineIdPrivacyOnRedirectedCalls"] = connected_line_id_privacy_on_redirected_calls
1933
+ try:
1934
+ result = api.session.rest_put(url, json=body, params=params)
1935
+ except WebexError as e:
1936
+ handle_rest_error(e)
1937
+ typer.echo(f"Updated.")
1938
+
1939
+
1940
+
1941
+ @app.command("configure-busy-voicemail")
1942
+ def configure_busy_voicemail(
1943
+ workspace_id: str = typer.Argument(help="workspaceId"),
1944
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1945
+ debug: bool = typer.Option(False, "--debug"),
1946
+ ):
1947
+ """Configure Busy Voicemail Greeting for a Place."""
1948
+ api = get_api(debug=debug)
1949
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/voicemail/actions/uploadBusyGreeting/invoke"
1950
+ params = {}
1951
+ org_id = get_org_id()
1952
+ if org_id is not None:
1953
+ params["orgId"] = org_id
1954
+ if json_body:
1955
+ body = json.loads(json_body)
1956
+ else:
1957
+ body = {}
1958
+ try:
1959
+ result = api.session.rest_post(url, json=body, params=params)
1960
+ except WebexError as e:
1961
+ handle_rest_error(e)
1962
+ print_json(result)
1963
+
1964
+
1965
+
1966
+ @app.command("configure-no-answer")
1967
+ def configure_no_answer(
1968
+ workspace_id: str = typer.Argument(help="workspaceId"),
1969
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1970
+ debug: bool = typer.Option(False, "--debug"),
1971
+ ):
1972
+ """Configure No Answer Voicemail Greeting for a Place."""
1973
+ api = get_api(debug=debug)
1974
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/voicemail/actions/uploadNoAnswerGreeting/invoke"
1975
+ params = {}
1976
+ org_id = get_org_id()
1977
+ if org_id is not None:
1978
+ params["orgId"] = org_id
1979
+ if json_body:
1980
+ body = json.loads(json_body)
1981
+ else:
1982
+ body = {}
1983
+ try:
1984
+ result = api.session.rest_post(url, json=body, params=params)
1985
+ except WebexError as e:
1986
+ handle_rest_error(e)
1987
+ print_json(result)
1988
+
1989
+
1990
+
1991
+ @app.command("create-criteria-sequential-ring")
1992
+ def create_criteria_sequential_ring(
1993
+ workspace_id: str = typer.Argument(help="workspaceId"),
1994
+ schedule_name: str = typer.Option(None, "--schedule-name", help="Name of the location's schedule which determines when the se"),
1995
+ schedule_type: str = typer.Option(None, "--schedule-type", help="Choices: holidays, businessHours"),
1996
+ schedule_level: str = typer.Option(None, "--schedule-level", help="Choices: GROUP"),
1997
+ calls_from: str = typer.Option(None, "--calls-from", help="(required) Choices: SELECT_PHONE_NUMBERS, ANY_PHONE_NUMBER"),
1998
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true` incoming calls from private numbers are allowed."),
1999
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true` incoming calls from unavailable numbers are allo"),
2000
+ ring_enabled: bool = typer.Option(None, "--ring-enabled/--no-ring-enabled", help="(required) When set to `true` sequential ringing is enabled for calls t"),
2001
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2002
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
2003
+ debug: bool = typer.Option(False, "--debug"),
2004
+ ):
2005
+ """Create Sequential Ring Criteria for a Workspace\n\nExample --json-body:\n '{"callsFrom":"SELECT_PHONE_NUMBERS","ringEnabled":true,"scheduleName":"...","scheduleType":"holidays","scheduleLevel":"GROUP","anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."]}'."""
2006
+ api = get_api(debug=debug)
2007
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/sequentialRing/criteria"
2008
+ params = {}
2009
+ org_id = get_org_id()
2010
+ if org_id is not None:
2011
+ params["orgId"] = org_id
2012
+ if json_body:
2013
+ body = json.loads(json_body)
2014
+ else:
2015
+ body = {}
2016
+ if schedule_name is not None:
2017
+ body["scheduleName"] = schedule_name
2018
+ if schedule_type is not None:
2019
+ body["scheduleType"] = schedule_type
2020
+ if schedule_level is not None:
2021
+ body["scheduleLevel"] = schedule_level
2022
+ if calls_from is not None:
2023
+ body["callsFrom"] = calls_from
2024
+ if anonymous_callers_enabled is not None:
2025
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
2026
+ if unavailable_callers_enabled is not None:
2027
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
2028
+ if ring_enabled is not None:
2029
+ body["ringEnabled"] = ring_enabled
2030
+ _missing = [f for f in ['callsFrom', 'ringEnabled'] if f not in body or body[f] is None]
2031
+ if _missing:
2032
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
2033
+ raise typer.Exit(1)
2034
+ try:
2035
+ result = api.session.rest_post(url, json=body, params=params)
2036
+ except WebexError as e:
2037
+ handle_rest_error(e)
2038
+ if output == "json":
2039
+ print_json(result)
2040
+ elif isinstance(result, dict) and "id" in result:
2041
+ typer.echo(f"Created: {result['id']}")
2042
+ elif not result or result == {}:
2043
+ typer.echo("Created.")
2044
+ else:
2045
+ print_json(result)
2046
+
2047
+
2048
+
2049
+ @app.command("list-sequential-ring")
2050
+ def list_sequential_ring(
2051
+ workspace_id: str = typer.Argument(help="workspaceId"),
2052
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
2053
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
2054
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
2055
+ debug: bool = typer.Option(False, "--debug"),
2056
+ ):
2057
+ """Retrieve Sequential Ring Settings for a Workspace."""
2058
+ api = get_api(debug=debug)
2059
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/sequentialRing"
2060
+ params = {}
2061
+ if limit > 0:
2062
+ params["max"] = limit
2063
+ if offset > 0:
2064
+ params["start"] = offset
2065
+ org_id = get_org_id()
2066
+ if org_id is not None:
2067
+ params["orgId"] = org_id
2068
+ result = None
2069
+ try:
2070
+ result = api.session.rest_get(url, params=params)
2071
+ except WebexError as e:
2072
+ handle_rest_error(e)
2073
+ result = result or []
2074
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
2075
+ if output == "json":
2076
+ print_json(items)
2077
+ else:
2078
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
2079
+
2080
+
2081
+
2082
+ @app.command("update-sequential-ring")
2083
+ def update_sequential_ring(
2084
+ workspace_id: str = typer.Argument(help="workspaceId"),
2085
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="When set to `true` sequential ring is enabled."),
2086
+ ring_base_location_first_enabled: bool = typer.Option(None, "--ring-base-location-first-enabled/--no-ring-base-location-first-enabled", help="When set to `true`, the webex calling primary line will ring"),
2087
+ base_location_number_of_rings: str = typer.Option(None, "--base-location-number-of-rings", help="The number of times the primary line will ring. `baseLocatio"),
2088
+ continue_if_base_location_is_busy_enabled: bool = typer.Option(None, "--continue-if-base-location-is-busy-enabled/--no-continue-if-base-location-is-busy-enabled", help="When set to `true` and the primary line is busy, the system"),
2089
+ calls_to_voicemail_enabled: bool = typer.Option(None, "--calls-to-voicemail-enabled/--no-calls-to-voicemail-enabled", help="When set to `true` calls are directed to voicemail."),
2090
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2091
+ debug: bool = typer.Option(False, "--debug"),
2092
+ ):
2093
+ """Modify Sequential Ring Settings for a Workspace\n\nExample --json-body:\n '{"enabled":true,"ringBaseLocationFirstEnabled":true,"baseLocationNumberOfRings":0,"continueIfBaseLocationIsBusyEnabled":true,"callsToVoicemailEnabled":true,"phoneNumbers":[{"phoneNumber":"...","answerConfirmationRequiredEnabled":"...","numberOfRings":"..."}]}'."""
2094
+ api = get_api(debug=debug)
2095
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/sequentialRing"
2096
+ params = {}
2097
+ org_id = get_org_id()
2098
+ if org_id is not None:
2099
+ params["orgId"] = org_id
2100
+ if json_body:
2101
+ body = json.loads(json_body)
2102
+ else:
2103
+ body = {}
2104
+ if enabled is not None:
2105
+ body["enabled"] = enabled
2106
+ if ring_base_location_first_enabled is not None:
2107
+ body["ringBaseLocationFirstEnabled"] = ring_base_location_first_enabled
2108
+ if base_location_number_of_rings is not None:
2109
+ body["baseLocationNumberOfRings"] = base_location_number_of_rings
2110
+ if continue_if_base_location_is_busy_enabled is not None:
2111
+ body["continueIfBaseLocationIsBusyEnabled"] = continue_if_base_location_is_busy_enabled
2112
+ if calls_to_voicemail_enabled is not None:
2113
+ body["callsToVoicemailEnabled"] = calls_to_voicemail_enabled
2114
+ try:
2115
+ result = api.session.rest_put(url, json=body, params=params)
2116
+ except WebexError as e:
2117
+ handle_rest_error(e)
2118
+ typer.echo(f"Updated.")
2119
+
2120
+
2121
+
2122
+ @app.command("list-simultaneous-ring")
2123
+ def list_simultaneous_ring(
2124
+ workspace_id: str = typer.Argument(help="workspaceId"),
2125
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
2126
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
2127
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
2128
+ debug: bool = typer.Option(False, "--debug"),
2129
+ ):
2130
+ """Retrieve Simultaneous Ring Settings for a Workspace."""
2131
+ api = get_api(debug=debug)
2132
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/simultaneousRing"
2133
+ params = {}
2134
+ if limit > 0:
2135
+ params["max"] = limit
2136
+ if offset > 0:
2137
+ params["start"] = offset
2138
+ org_id = get_org_id()
2139
+ if org_id is not None:
2140
+ params["orgId"] = org_id
2141
+ result = None
2142
+ try:
2143
+ result = api.session.rest_get(url, params=params)
2144
+ except WebexError as e:
2145
+ handle_rest_error(e)
2146
+ result = result or []
2147
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
2148
+ if output == "json":
2149
+ print_json(items)
2150
+ else:
2151
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
2152
+
2153
+
2154
+
2155
+ @app.command("update-simultaneous-ring")
2156
+ def update_simultaneous_ring(
2157
+ workspace_id: str = typer.Argument(help="workspaceId"),
2158
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Simultaneous Ring is enabled or not."),
2159
+ do_not_ring_if_on_call_enabled: bool = typer.Option(None, "--do-not-ring-if-on-call-enabled/--no-do-not-ring-if-on-call-enabled", help="When set to `true`, the configured phone numbers won't ring"),
2160
+ criterias_enabled: bool = typer.Option(None, "--criterias-enabled/--no-criterias-enabled", help="When `true`, enables the selected schedule for simultaneous"),
2161
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2162
+ debug: bool = typer.Option(False, "--debug"),
2163
+ ):
2164
+ """Modify Simultaneous Ring Settings for a Workspace\n\nExample --json-body:\n '{"criteriasEnabled":true,"enabled":true,"doNotRingIfOnCallEnabled":true,"phoneNumbers":[{"phoneNumber":"...","answerConfirmationRequiredEnabled":"..."}]}'."""
2165
+ api = get_api(debug=debug)
2166
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/simultaneousRing"
2167
+ params = {}
2168
+ org_id = get_org_id()
2169
+ if org_id is not None:
2170
+ params["orgId"] = org_id
2171
+ if json_body:
2172
+ body = json.loads(json_body)
2173
+ else:
2174
+ body = {}
2175
+ if enabled is not None:
2176
+ body["enabled"] = enabled
2177
+ if do_not_ring_if_on_call_enabled is not None:
2178
+ body["doNotRingIfOnCallEnabled"] = do_not_ring_if_on_call_enabled
2179
+ if criterias_enabled is not None:
2180
+ body["criteriasEnabled"] = criterias_enabled
2181
+ try:
2182
+ result = api.session.rest_put(url, json=body, params=params)
2183
+ except WebexError as e:
2184
+ handle_rest_error(e)
2185
+ typer.echo(f"Updated.")
2186
+
2187
+
2188
+
2189
+ @app.command("show-criteria-simultaneous-ring")
2190
+ def show_criteria_simultaneous_ring(
2191
+ workspace_id: str = typer.Argument(help="workspaceId"),
2192
+ id: str = typer.Argument(help="id"),
2193
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
2194
+ debug: bool = typer.Option(False, "--debug"),
2195
+ ):
2196
+ """Retrieve Simultaneous Ring Criteria for a Workspace."""
2197
+ api = get_api(debug=debug)
2198
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/simultaneousRing/criteria/{id}"
2199
+ params = {}
2200
+ org_id = get_org_id()
2201
+ if org_id is not None:
2202
+ params["orgId"] = org_id
2203
+ try:
2204
+ result = api.session.rest_get(url, params=params)
2205
+ except WebexError as e:
2206
+ handle_rest_error(e)
2207
+ if output == "json":
2208
+ print_json(result)
2209
+ else:
2210
+ if isinstance(result, dict):
2211
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
2212
+ elif isinstance(result, list):
2213
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
2214
+ else:
2215
+ print_json(result)
2216
+
2217
+
2218
+
2219
+ @app.command("update-criteria-simultaneous-ring")
2220
+ def update_criteria_simultaneous_ring(
2221
+ workspace_id: str = typer.Argument(help="workspaceId"),
2222
+ id: str = typer.Argument(help="id"),
2223
+ schedule_name: str = typer.Option(None, "--schedule-name", help="Name of the location's schedule which determines when the si"),
2224
+ schedule_type: str = typer.Option(None, "--schedule-type", help="Choices: businessHours, holidays"),
2225
+ schedule_level: str = typer.Option(None, "--schedule-level", help="Choices: GROUP"),
2226
+ calls_from: str = typer.Option(None, "--calls-from", help="Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS"),
2227
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables calls from anonymous callers."),
2228
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables calls even if callers are unavailable."),
2229
+ ring_enabled: bool = typer.Option(None, "--ring-enabled/--no-ring-enabled", help="When set to `true` simultaneous ringing criteria is enabled"),
2230
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2231
+ debug: bool = typer.Option(False, "--debug"),
2232
+ ):
2233
+ """Modify Simultaneous Ring Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","callsFrom":"ANY_PHONE_NUMBER","anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."],"ringEnabled":true}'."""
2234
+ api = get_api(debug=debug)
2235
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/simultaneousRing/criteria/{id}"
2236
+ params = {}
2237
+ org_id = get_org_id()
2238
+ if org_id is not None:
2239
+ params["orgId"] = org_id
2240
+ if json_body:
2241
+ body = json.loads(json_body)
2242
+ else:
2243
+ body = {}
2244
+ if schedule_name is not None:
2245
+ body["scheduleName"] = schedule_name
2246
+ if schedule_type is not None:
2247
+ body["scheduleType"] = schedule_type
2248
+ if schedule_level is not None:
2249
+ body["scheduleLevel"] = schedule_level
2250
+ if calls_from is not None:
2251
+ body["callsFrom"] = calls_from
2252
+ if anonymous_callers_enabled is not None:
2253
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
2254
+ if unavailable_callers_enabled is not None:
2255
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
2256
+ if ring_enabled is not None:
2257
+ body["ringEnabled"] = ring_enabled
2258
+ try:
2259
+ result = api.session.rest_put(url, json=body, params=params)
2260
+ except WebexError as e:
2261
+ handle_rest_error(e)
2262
+ typer.echo(f"Updated.")
2263
+
2264
+
2265
+
2266
+ @app.command("delete-criteria-simultaneous-ring")
2267
+ def delete_criteria_simultaneous_ring(
2268
+ workspace_id: str = typer.Argument(help="workspaceId"),
2269
+ id: str = typer.Argument(help="id"),
2270
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
2271
+ debug: bool = typer.Option(False, "--debug"),
2272
+ ):
2273
+ """Delete Simultaneous Ring Criteria for a Workspace."""
2274
+ if not force:
2275
+ typer.confirm(f"Delete {id}?", abort=True)
2276
+ api = get_api(debug=debug)
2277
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/simultaneousRing/criteria/{id}"
2278
+ params = {}
2279
+ org_id = get_org_id()
2280
+ if org_id is not None:
2281
+ params["orgId"] = org_id
2282
+ try:
2283
+ api.session.rest_delete(url, params=params)
2284
+ except WebexError as e:
2285
+ handle_rest_error(e)
2286
+ typer.echo(f"Deleted: {id}")
2287
+
2288
+
2289
+
2290
+ @app.command("create-criteria-simultaneous-ring")
2291
+ def create_criteria_simultaneous_ring(
2292
+ workspace_id: str = typer.Argument(help="workspaceId"),
2293
+ schedule_name: str = typer.Option(None, "--schedule-name", help="(required) Name of the location's schedule which determines when the si"),
2294
+ schedule_type: str = typer.Option(None, "--schedule-type", help="(required) Choices: businessHours, holidays"),
2295
+ schedule_level: str = typer.Option(None, "--schedule-level", help="(required) Choices: GROUP"),
2296
+ calls_from: str = typer.Option(None, "--calls-from", help="(required) Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS"),
2297
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables calls from anonymous callers. Value for"),
2298
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables calls even if callers are unavailable."),
2299
+ ring_enabled: bool = typer.Option(None, "--ring-enabled/--no-ring-enabled", help="(required) When set to `true` simultaneous ringing criteria is enabled"),
2300
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2301
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
2302
+ debug: bool = typer.Option(False, "--debug"),
2303
+ ):
2304
+ """Create Simultaneous Ring Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","callsFrom":"ANY_PHONE_NUMBER","ringEnabled":true,"anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."]}'."""
2305
+ api = get_api(debug=debug)
2306
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/simultaneousRing/criteria"
2307
+ params = {}
2308
+ org_id = get_org_id()
2309
+ if org_id is not None:
2310
+ params["orgId"] = org_id
2311
+ if json_body:
2312
+ body = json.loads(json_body)
2313
+ else:
2314
+ body = {}
2315
+ if schedule_name is not None:
2316
+ body["scheduleName"] = schedule_name
2317
+ if schedule_type is not None:
2318
+ body["scheduleType"] = schedule_type
2319
+ if schedule_level is not None:
2320
+ body["scheduleLevel"] = schedule_level
2321
+ if calls_from is not None:
2322
+ body["callsFrom"] = calls_from
2323
+ if anonymous_callers_enabled is not None:
2324
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
2325
+ if unavailable_callers_enabled is not None:
2326
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
2327
+ if ring_enabled is not None:
2328
+ body["ringEnabled"] = ring_enabled
2329
+ _missing = [f for f in ['scheduleName', 'scheduleType', 'scheduleLevel', 'callsFrom', 'ringEnabled'] if f not in body or body[f] is None]
2330
+ if _missing:
2331
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
2332
+ raise typer.Exit(1)
2333
+ try:
2334
+ result = api.session.rest_post(url, json=body, params=params)
2335
+ except WebexError as e:
2336
+ handle_rest_error(e)
2337
+ if output == "json":
2338
+ print_json(result)
2339
+ elif isinstance(result, dict) and "id" in result:
2340
+ typer.echo(f"Created: {result['id']}")
2341
+ elif not result or result == {}:
2342
+ typer.echo("Created.")
2343
+ else:
2344
+ print_json(result)
2345
+
2346
+
2347
+
2348
+ @app.command("list-selective-reject")
2349
+ def list_selective_reject(
2350
+ workspace_id: str = typer.Argument(help="workspaceId"),
2351
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
2352
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
2353
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
2354
+ debug: bool = typer.Option(False, "--debug"),
2355
+ ):
2356
+ """Retrieve Selective Reject Settings for a Workspace."""
2357
+ api = get_api(debug=debug)
2358
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveReject"
2359
+ params = {}
2360
+ if limit > 0:
2361
+ params["max"] = limit
2362
+ if offset > 0:
2363
+ params["start"] = offset
2364
+ org_id = get_org_id()
2365
+ if org_id is not None:
2366
+ params["orgId"] = org_id
2367
+ result = None
2368
+ try:
2369
+ result = api.session.rest_get(url, params=params)
2370
+ except WebexError as e:
2371
+ handle_rest_error(e)
2372
+ result = result or []
2373
+ items = result.get("criteria", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
2374
+ if output == "json":
2375
+ print_json(items)
2376
+ else:
2377
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
2378
+
2379
+
2380
+
2381
+ @app.command("update-selective-reject")
2382
+ def update_selective_reject(
2383
+ workspace_id: str = typer.Argument(help="workspaceId"),
2384
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="if `true`, selective reject is enabled."),
2385
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2386
+ debug: bool = typer.Option(False, "--debug"),
2387
+ ):
2388
+ """Modify Selective Reject Settings for a Workspace."""
2389
+ api = get_api(debug=debug)
2390
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveReject"
2391
+ params = {}
2392
+ org_id = get_org_id()
2393
+ if org_id is not None:
2394
+ params["orgId"] = org_id
2395
+ if json_body:
2396
+ body = json.loads(json_body)
2397
+ else:
2398
+ body = {}
2399
+ if enabled is not None:
2400
+ body["enabled"] = enabled
2401
+ try:
2402
+ result = api.session.rest_put(url, json=body, params=params)
2403
+ except WebexError as e:
2404
+ handle_rest_error(e)
2405
+ typer.echo(f"Updated.")
2406
+
2407
+
2408
+
2409
+ @app.command("show-criteria-selective-reject")
2410
+ def show_criteria_selective_reject(
2411
+ workspace_id: str = typer.Argument(help="workspaceId"),
2412
+ id: str = typer.Argument(help="id"),
2413
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
2414
+ debug: bool = typer.Option(False, "--debug"),
2415
+ ):
2416
+ """Retrieve Selective Reject Criteria for a Workspace."""
2417
+ api = get_api(debug=debug)
2418
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveReject/criteria/{id}"
2419
+ params = {}
2420
+ org_id = get_org_id()
2421
+ if org_id is not None:
2422
+ params["orgId"] = org_id
2423
+ try:
2424
+ result = api.session.rest_get(url, params=params)
2425
+ except WebexError as e:
2426
+ handle_rest_error(e)
2427
+ if output == "json":
2428
+ print_json(result)
2429
+ else:
2430
+ if isinstance(result, dict):
2431
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
2432
+ elif isinstance(result, list):
2433
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
2434
+ else:
2435
+ print_json(result)
2436
+
2437
+
2438
+
2439
+ @app.command("update-criteria-selective-reject")
2440
+ def update_criteria_selective_reject(
2441
+ workspace_id: str = typer.Argument(help="workspaceId"),
2442
+ id: str = typer.Argument(help="id"),
2443
+ schedule_name: str = typer.Option(None, "--schedule-name", help="Name of the location's schedule which determines when the se"),
2444
+ schedule_type: str = typer.Option(None, "--schedule-type", help="Choices: businessHours, holidays"),
2445
+ schedule_level: str = typer.Option(None, "--schedule-level", help="Choices: GROUP"),
2446
+ calls_from: str = typer.Option(None, "--calls-from", help="Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS, FORWARDED"),
2447
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables calls from anonymous callers."),
2448
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables calls even if callers are unavailable."),
2449
+ reject_enabled: bool = typer.Option(None, "--reject-enabled/--no-reject-enabled", help="Choose to reject (if `rejectEnabled` = `true`) or not to rej"),
2450
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2451
+ debug: bool = typer.Option(False, "--debug"),
2452
+ ):
2453
+ """Modify Selective Reject Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","callsFrom":"ANY_PHONE_NUMBER","anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."],"rejectEnabled":true}'."""
2454
+ api = get_api(debug=debug)
2455
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveReject/criteria/{id}"
2456
+ params = {}
2457
+ org_id = get_org_id()
2458
+ if org_id is not None:
2459
+ params["orgId"] = org_id
2460
+ if json_body:
2461
+ body = json.loads(json_body)
2462
+ else:
2463
+ body = {}
2464
+ if schedule_name is not None:
2465
+ body["scheduleName"] = schedule_name
2466
+ if schedule_type is not None:
2467
+ body["scheduleType"] = schedule_type
2468
+ if schedule_level is not None:
2469
+ body["scheduleLevel"] = schedule_level
2470
+ if calls_from is not None:
2471
+ body["callsFrom"] = calls_from
2472
+ if anonymous_callers_enabled is not None:
2473
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
2474
+ if unavailable_callers_enabled is not None:
2475
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
2476
+ if reject_enabled is not None:
2477
+ body["rejectEnabled"] = reject_enabled
2478
+ try:
2479
+ result = api.session.rest_put(url, json=body, params=params)
2480
+ except WebexError as e:
2481
+ handle_rest_error(e)
2482
+ typer.echo(f"Updated.")
2483
+
2484
+
2485
+
2486
+ @app.command("delete-criteria-selective-reject")
2487
+ def delete_criteria_selective_reject(
2488
+ workspace_id: str = typer.Argument(help="workspaceId"),
2489
+ id: str = typer.Argument(help="id"),
2490
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
2491
+ debug: bool = typer.Option(False, "--debug"),
2492
+ ):
2493
+ """Delete Selective Reject Criteria for a Workspace."""
2494
+ if not force:
2495
+ typer.confirm(f"Delete {id}?", abort=True)
2496
+ api = get_api(debug=debug)
2497
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveReject/criteria/{id}"
2498
+ params = {}
2499
+ org_id = get_org_id()
2500
+ if org_id is not None:
2501
+ params["orgId"] = org_id
2502
+ try:
2503
+ api.session.rest_delete(url, params=params)
2504
+ except WebexError as e:
2505
+ handle_rest_error(e)
2506
+ typer.echo(f"Deleted: {id}")
2507
+
2508
+
2509
+
2510
+ @app.command("create-criteria-selective-reject")
2511
+ def create_criteria_selective_reject(
2512
+ workspace_id: str = typer.Argument(help="workspaceId"),
2513
+ schedule_name: str = typer.Option(None, "--schedule-name", help="(required) Name of the location's schedule which determines when the se"),
2514
+ schedule_type: str = typer.Option(None, "--schedule-type", help="(required) Choices: businessHours, holidays"),
2515
+ schedule_level: str = typer.Option(None, "--schedule-level", help="(required) Choices: GROUP"),
2516
+ calls_from: str = typer.Option(None, "--calls-from", help="(required) Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS, FORWARDED"),
2517
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables calls from anonymous callers. Value for"),
2518
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables calls even if callers are unavailable."),
2519
+ reject_enabled: bool = typer.Option(None, "--reject-enabled/--no-reject-enabled", help="(required) Choose to reject (if `rejectEnabled` = `true`) or not to rej"),
2520
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2521
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
2522
+ debug: bool = typer.Option(False, "--debug"),
2523
+ ):
2524
+ """Create Selective Reject Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","callsFrom":"ANY_PHONE_NUMBER","rejectEnabled":true,"anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."]}'."""
2525
+ api = get_api(debug=debug)
2526
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveReject/criteria"
2527
+ params = {}
2528
+ org_id = get_org_id()
2529
+ if org_id is not None:
2530
+ params["orgId"] = org_id
2531
+ if json_body:
2532
+ body = json.loads(json_body)
2533
+ else:
2534
+ body = {}
2535
+ if schedule_name is not None:
2536
+ body["scheduleName"] = schedule_name
2537
+ if schedule_type is not None:
2538
+ body["scheduleType"] = schedule_type
2539
+ if schedule_level is not None:
2540
+ body["scheduleLevel"] = schedule_level
2541
+ if calls_from is not None:
2542
+ body["callsFrom"] = calls_from
2543
+ if anonymous_callers_enabled is not None:
2544
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
2545
+ if unavailable_callers_enabled is not None:
2546
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
2547
+ if reject_enabled is not None:
2548
+ body["rejectEnabled"] = reject_enabled
2549
+ _missing = [f for f in ['scheduleName', 'scheduleType', 'scheduleLevel', 'callsFrom', 'rejectEnabled'] if f not in body or body[f] is None]
2550
+ if _missing:
2551
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
2552
+ raise typer.Exit(1)
2553
+ try:
2554
+ result = api.session.rest_post(url, json=body, params=params)
2555
+ except WebexError as e:
2556
+ handle_rest_error(e)
2557
+ if output == "json":
2558
+ print_json(result)
2559
+ elif isinstance(result, dict) and "id" in result:
2560
+ typer.echo(f"Created: {result['id']}")
2561
+ elif not result or result == {}:
2562
+ typer.echo("Created.")
2563
+ else:
2564
+ print_json(result)
2565
+
2566
+
2567
+
2568
+ @app.command("update-numbers")
2569
+ def update_numbers(
2570
+ workspace_id: str = typer.Argument(help="workspaceId"),
2571
+ distinctive_ring_enabled: bool = typer.Option(None, "--distinctive-ring-enabled/--no-distinctive-ring-enabled", help="Enables a distinctive ring pattern for the person."),
2572
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2573
+ debug: bool = typer.Option(False, "--debug"),
2574
+ ):
2575
+ """Assign or Unassign numbers associated with a specific workspace\n\nExample --json-body:\n '{"phoneNumbers":[{"primary":"...","action":"...","directNumber":"...","extension":"...","ringPattern":"..."}],"distinctiveRingEnabled":true}'."""
2576
+ api = get_api(debug=debug)
2577
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/numbers"
2578
+ params = {}
2579
+ org_id = get_org_id()
2580
+ if org_id is not None:
2581
+ params["orgId"] = org_id
2582
+ if json_body:
2583
+ body = json.loads(json_body)
2584
+ else:
2585
+ body = {}
2586
+ if distinctive_ring_enabled is not None:
2587
+ body["distinctiveRingEnabled"] = distinctive_ring_enabled
2588
+ try:
2589
+ result = api.session.rest_put(url, json=body, params=params)
2590
+ except WebexError as e:
2591
+ handle_rest_error(e)
2592
+ typer.echo(f"Updated.")
2593
+
2594
+
2595
+
2596
+ @app.command("list-selective-accept")
2597
+ def list_selective_accept(
2598
+ workspace_id: str = typer.Argument(help="workspaceId"),
2599
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
2600
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
2601
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
2602
+ debug: bool = typer.Option(False, "--debug"),
2603
+ ):
2604
+ """Retrieve Selective Accept Settings for a Workspace."""
2605
+ api = get_api(debug=debug)
2606
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveAccept"
2607
+ params = {}
2608
+ if limit > 0:
2609
+ params["max"] = limit
2610
+ if offset > 0:
2611
+ params["start"] = offset
2612
+ org_id = get_org_id()
2613
+ if org_id is not None:
2614
+ params["orgId"] = org_id
2615
+ result = None
2616
+ try:
2617
+ result = api.session.rest_get(url, params=params)
2618
+ except WebexError as e:
2619
+ handle_rest_error(e)
2620
+ result = result or []
2621
+ items = result.get("criteria", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
2622
+ if output == "json":
2623
+ print_json(items)
2624
+ else:
2625
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
2626
+
2627
+
2628
+
2629
+ @app.command("update-selective-accept")
2630
+ def update_selective_accept(
2631
+ workspace_id: str = typer.Argument(help="workspaceId"),
2632
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="indicates whether selective accept is enabled or not."),
2633
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2634
+ debug: bool = typer.Option(False, "--debug"),
2635
+ ):
2636
+ """Modify Selective Accept Settings for a Workspace."""
2637
+ api = get_api(debug=debug)
2638
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveAccept"
2639
+ params = {}
2640
+ org_id = get_org_id()
2641
+ if org_id is not None:
2642
+ params["orgId"] = org_id
2643
+ if json_body:
2644
+ body = json.loads(json_body)
2645
+ else:
2646
+ body = {}
2647
+ if enabled is not None:
2648
+ body["enabled"] = enabled
2649
+ try:
2650
+ result = api.session.rest_put(url, json=body, params=params)
2651
+ except WebexError as e:
2652
+ handle_rest_error(e)
2653
+ typer.echo(f"Updated.")
2654
+
2655
+
2656
+
2657
+ @app.command("show-criteria-selective-accept")
2658
+ def show_criteria_selective_accept(
2659
+ workspace_id: str = typer.Argument(help="workspaceId"),
2660
+ id: str = typer.Argument(help="id"),
2661
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
2662
+ debug: bool = typer.Option(False, "--debug"),
2663
+ ):
2664
+ """Retrieve Selective Accept Criteria for a Workspace."""
2665
+ api = get_api(debug=debug)
2666
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveAccept/criteria/{id}"
2667
+ params = {}
2668
+ org_id = get_org_id()
2669
+ if org_id is not None:
2670
+ params["orgId"] = org_id
2671
+ try:
2672
+ result = api.session.rest_get(url, params=params)
2673
+ except WebexError as e:
2674
+ handle_rest_error(e)
2675
+ if output == "json":
2676
+ print_json(result)
2677
+ else:
2678
+ if isinstance(result, dict):
2679
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
2680
+ elif isinstance(result, list):
2681
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
2682
+ else:
2683
+ print_json(result)
2684
+
2685
+
2686
+
2687
+ @app.command("update-criteria-selective-accept")
2688
+ def update_criteria_selective_accept(
2689
+ workspace_id: str = typer.Argument(help="workspaceId"),
2690
+ id: str = typer.Argument(help="id"),
2691
+ schedule_name: str = typer.Option(None, "--schedule-name", help="Name of the location's schedule which determines when the se"),
2692
+ schedule_type: str = typer.Option(None, "--schedule-type", help="Choices: businessHours, holidays"),
2693
+ schedule_level: str = typer.Option(None, "--schedule-level", help="Choices: GROUP"),
2694
+ calls_from: str = typer.Option(None, "--calls-from", help="Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS, FORWARDED"),
2695
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables calls from anonymous callers."),
2696
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables calls even if callers are unavailable."),
2697
+ accept_enabled: bool = typer.Option(None, "--accept-enabled/--no-accept-enabled", help="Choose to accept (if `acceptEnabled` = `true`) or not to acc"),
2698
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2699
+ debug: bool = typer.Option(False, "--debug"),
2700
+ ):
2701
+ """Modify Selective Accept Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","callsFrom":"ANY_PHONE_NUMBER","anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."],"acceptEnabled":true}'."""
2702
+ api = get_api(debug=debug)
2703
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveAccept/criteria/{id}"
2704
+ params = {}
2705
+ org_id = get_org_id()
2706
+ if org_id is not None:
2707
+ params["orgId"] = org_id
2708
+ if json_body:
2709
+ body = json.loads(json_body)
2710
+ else:
2711
+ body = {}
2712
+ if schedule_name is not None:
2713
+ body["scheduleName"] = schedule_name
2714
+ if schedule_type is not None:
2715
+ body["scheduleType"] = schedule_type
2716
+ if schedule_level is not None:
2717
+ body["scheduleLevel"] = schedule_level
2718
+ if calls_from is not None:
2719
+ body["callsFrom"] = calls_from
2720
+ if anonymous_callers_enabled is not None:
2721
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
2722
+ if unavailable_callers_enabled is not None:
2723
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
2724
+ if accept_enabled is not None:
2725
+ body["acceptEnabled"] = accept_enabled
2726
+ try:
2727
+ result = api.session.rest_put(url, json=body, params=params)
2728
+ except WebexError as e:
2729
+ handle_rest_error(e)
2730
+ typer.echo(f"Updated.")
2731
+
2732
+
2733
+
2734
+ @app.command("delete-criteria-selective-accept")
2735
+ def delete_criteria_selective_accept(
2736
+ workspace_id: str = typer.Argument(help="workspaceId"),
2737
+ id: str = typer.Argument(help="id"),
2738
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
2739
+ debug: bool = typer.Option(False, "--debug"),
2740
+ ):
2741
+ """Delete Selective Accept Criteria for a Workspace."""
2742
+ if not force:
2743
+ typer.confirm(f"Delete {id}?", abort=True)
2744
+ api = get_api(debug=debug)
2745
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveAccept/criteria/{id}"
2746
+ params = {}
2747
+ org_id = get_org_id()
2748
+ if org_id is not None:
2749
+ params["orgId"] = org_id
2750
+ try:
2751
+ api.session.rest_delete(url, params=params)
2752
+ except WebexError as e:
2753
+ handle_rest_error(e)
2754
+ typer.echo(f"Deleted: {id}")
2755
+
2756
+
2757
+
2758
+ @app.command("create-criteria-selective-accept")
2759
+ def create_criteria_selective_accept(
2760
+ workspace_id: str = typer.Argument(help="workspaceId"),
2761
+ schedule_name: str = typer.Option(None, "--schedule-name", help="(required) Name of the location's schedule which determines when the se"),
2762
+ schedule_type: str = typer.Option(None, "--schedule-type", help="(required) Choices: businessHours, holidays"),
2763
+ schedule_level: str = typer.Option(None, "--schedule-level", help="(required) Choices: GROUP"),
2764
+ calls_from: str = typer.Option(None, "--calls-from", help="(required) Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS, FORWARDED"),
2765
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables calls from anonymous callers. Value for"),
2766
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables calls even if callers are unavailable."),
2767
+ accept_enabled: bool = typer.Option(None, "--accept-enabled/--no-accept-enabled", help="(required) Choose to accept (if `acceptEnabled` = `true`) or not to acc"),
2768
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2769
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
2770
+ debug: bool = typer.Option(False, "--debug"),
2771
+ ):
2772
+ """Create Selective Accept Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","callsFrom":"ANY_PHONE_NUMBER","acceptEnabled":true,"anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."]}'."""
2773
+ api = get_api(debug=debug)
2774
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveAccept/criteria"
2775
+ params = {}
2776
+ org_id = get_org_id()
2777
+ if org_id is not None:
2778
+ params["orgId"] = org_id
2779
+ if json_body:
2780
+ body = json.loads(json_body)
2781
+ else:
2782
+ body = {}
2783
+ if schedule_name is not None:
2784
+ body["scheduleName"] = schedule_name
2785
+ if schedule_type is not None:
2786
+ body["scheduleType"] = schedule_type
2787
+ if schedule_level is not None:
2788
+ body["scheduleLevel"] = schedule_level
2789
+ if calls_from is not None:
2790
+ body["callsFrom"] = calls_from
2791
+ if anonymous_callers_enabled is not None:
2792
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
2793
+ if unavailable_callers_enabled is not None:
2794
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
2795
+ if accept_enabled is not None:
2796
+ body["acceptEnabled"] = accept_enabled
2797
+ _missing = [f for f in ['scheduleName', 'scheduleType', 'scheduleLevel', 'callsFrom', 'acceptEnabled'] if f not in body or body[f] is None]
2798
+ if _missing:
2799
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
2800
+ raise typer.Exit(1)
2801
+ try:
2802
+ result = api.session.rest_post(url, json=body, params=params)
2803
+ except WebexError as e:
2804
+ handle_rest_error(e)
2805
+ if output == "json":
2806
+ print_json(result)
2807
+ elif isinstance(result, dict) and "id" in result:
2808
+ typer.echo(f"Created: {result['id']}")
2809
+ elif not result or result == {}:
2810
+ typer.echo("Created.")
2811
+ else:
2812
+ print_json(result)
2813
+
2814
+
2815
+
2816
+ @app.command("list-priority-alert")
2817
+ def list_priority_alert(
2818
+ workspace_id: str = typer.Argument(help="workspaceId"),
2819
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
2820
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
2821
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
2822
+ debug: bool = typer.Option(False, "--debug"),
2823
+ ):
2824
+ """Retrieve Priority Alert Settings for a Workspace."""
2825
+ api = get_api(debug=debug)
2826
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/priorityAlert"
2827
+ params = {}
2828
+ if limit > 0:
2829
+ params["max"] = limit
2830
+ if offset > 0:
2831
+ params["start"] = offset
2832
+ org_id = get_org_id()
2833
+ if org_id is not None:
2834
+ params["orgId"] = org_id
2835
+ result = None
2836
+ try:
2837
+ result = api.session.rest_get(url, params=params)
2838
+ except WebexError as e:
2839
+ handle_rest_error(e)
2840
+ result = result or []
2841
+ items = result.get("criteria", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
2842
+ if output == "json":
2843
+ print_json(items)
2844
+ else:
2845
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
2846
+
2847
+
2848
+
2849
+ @app.command("update-priority-alert")
2850
+ def update_priority_alert(
2851
+ workspace_id: str = typer.Argument(help="workspaceId"),
2852
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if the Priority Alert feature is enabled."),
2853
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2854
+ debug: bool = typer.Option(False, "--debug"),
2855
+ ):
2856
+ """Configure Priority Alert Settings for a Workspace."""
2857
+ api = get_api(debug=debug)
2858
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/priorityAlert"
2859
+ params = {}
2860
+ org_id = get_org_id()
2861
+ if org_id is not None:
2862
+ params["orgId"] = org_id
2863
+ if json_body:
2864
+ body = json.loads(json_body)
2865
+ else:
2866
+ body = {}
2867
+ if enabled is not None:
2868
+ body["enabled"] = enabled
2869
+ try:
2870
+ result = api.session.rest_put(url, json=body, params=params)
2871
+ except WebexError as e:
2872
+ handle_rest_error(e)
2873
+ typer.echo(f"Updated.")
2874
+
2875
+
2876
+
2877
+ @app.command("show-criteria-priority-alert")
2878
+ def show_criteria_priority_alert(
2879
+ workspace_id: str = typer.Argument(help="workspaceId"),
2880
+ id: str = typer.Argument(help="id"),
2881
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
2882
+ debug: bool = typer.Option(False, "--debug"),
2883
+ ):
2884
+ """Retrieve Priority Alert Criteria for a Workspace."""
2885
+ api = get_api(debug=debug)
2886
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/priorityAlert/criteria/{id}"
2887
+ params = {}
2888
+ org_id = get_org_id()
2889
+ if org_id is not None:
2890
+ params["orgId"] = org_id
2891
+ try:
2892
+ result = api.session.rest_get(url, params=params)
2893
+ except WebexError as e:
2894
+ handle_rest_error(e)
2895
+ if output == "json":
2896
+ print_json(result)
2897
+ else:
2898
+ if isinstance(result, dict):
2899
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
2900
+ elif isinstance(result, list):
2901
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
2902
+ else:
2903
+ print_json(result)
2904
+
2905
+
2906
+
2907
+ @app.command("update-criteria-priority-alert")
2908
+ def update_criteria_priority_alert(
2909
+ workspace_id: str = typer.Argument(help="workspaceId"),
2910
+ id: str = typer.Argument(help="id"),
2911
+ schedule_name: str = typer.Option(None, "--schedule-name", help="Name of the location's schedule which determines when the pr"),
2912
+ schedule_type: str = typer.Option(None, "--schedule-type", help="Choices: businessHours, holidays"),
2913
+ schedule_level: str = typer.Option(None, "--schedule-level", help="Choices: GROUP"),
2914
+ calls_from: str = typer.Option(None, "--calls-from", help="Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS"),
2915
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables calls from anonymous callers."),
2916
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables calls even if callers are unavailable."),
2917
+ notification_enabled: bool = typer.Option(None, "--notification-enabled/--no-notification-enabled", help="When set to `true` priority alerting criteria is enabled for"),
2918
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2919
+ debug: bool = typer.Option(False, "--debug"),
2920
+ ):
2921
+ """Modify Priority Alert Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","callsFrom":"ANY_PHONE_NUMBER","anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."],"notificationEnabled":true}'."""
2922
+ api = get_api(debug=debug)
2923
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/priorityAlert/criteria/{id}"
2924
+ params = {}
2925
+ org_id = get_org_id()
2926
+ if org_id is not None:
2927
+ params["orgId"] = org_id
2928
+ if json_body:
2929
+ body = json.loads(json_body)
2930
+ else:
2931
+ body = {}
2932
+ if schedule_name is not None:
2933
+ body["scheduleName"] = schedule_name
2934
+ if schedule_type is not None:
2935
+ body["scheduleType"] = schedule_type
2936
+ if schedule_level is not None:
2937
+ body["scheduleLevel"] = schedule_level
2938
+ if calls_from is not None:
2939
+ body["callsFrom"] = calls_from
2940
+ if anonymous_callers_enabled is not None:
2941
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
2942
+ if unavailable_callers_enabled is not None:
2943
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
2944
+ if notification_enabled is not None:
2945
+ body["notificationEnabled"] = notification_enabled
2946
+ try:
2947
+ result = api.session.rest_put(url, json=body, params=params)
2948
+ except WebexError as e:
2949
+ handle_rest_error(e)
2950
+ typer.echo(f"Updated.")
2951
+
2952
+
2953
+
2954
+ @app.command("delete-criteria-priority-alert")
2955
+ def delete_criteria_priority_alert(
2956
+ workspace_id: str = typer.Argument(help="workspaceId"),
2957
+ id: str = typer.Argument(help="id"),
2958
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
2959
+ debug: bool = typer.Option(False, "--debug"),
2960
+ ):
2961
+ """Delete Priority Alert Criteria for a Workspace."""
2962
+ if not force:
2963
+ typer.confirm(f"Delete {id}?", abort=True)
2964
+ api = get_api(debug=debug)
2965
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/priorityAlert/criteria/{id}"
2966
+ params = {}
2967
+ org_id = get_org_id()
2968
+ if org_id is not None:
2969
+ params["orgId"] = org_id
2970
+ try:
2971
+ api.session.rest_delete(url, params=params)
2972
+ except WebexError as e:
2973
+ handle_rest_error(e)
2974
+ typer.echo(f"Deleted: {id}")
2975
+
2976
+
2977
+
2978
+ @app.command("create-criteria-priority-alert")
2979
+ def create_criteria_priority_alert(
2980
+ workspace_id: str = typer.Argument(help="workspaceId"),
2981
+ schedule_name: str = typer.Option(None, "--schedule-name", help="(required) Name of the location's schedule which determines when the pr"),
2982
+ schedule_type: str = typer.Option(None, "--schedule-type", help="(required) Choices: businessHours, holidays"),
2983
+ schedule_level: str = typer.Option(None, "--schedule-level", help="(required) Choices: GROUP"),
2984
+ calls_from: str = typer.Option(None, "--calls-from", help="(required) Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS"),
2985
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables calls from anonymous callers. Value for"),
2986
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables calls even if callers are unavailable."),
2987
+ notification_enabled: bool = typer.Option(None, "--notification-enabled/--no-notification-enabled", help="(required) When set to `true` priority alerting criteria is enabled for"),
2988
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
2989
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
2990
+ debug: bool = typer.Option(False, "--debug"),
2991
+ ):
2992
+ """Create Priority Alert Criteria for a Workspace\n\nExample --json-body:\n '{"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","callsFrom":"ANY_PHONE_NUMBER","notificationEnabled":true,"anonymousCallersEnabled":true,"unavailableCallersEnabled":true,"phoneNumbers":["..."]}'."""
2993
+ api = get_api(debug=debug)
2994
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/priorityAlert/criteria"
2995
+ params = {}
2996
+ org_id = get_org_id()
2997
+ if org_id is not None:
2998
+ params["orgId"] = org_id
2999
+ if json_body:
3000
+ body = json.loads(json_body)
3001
+ else:
3002
+ body = {}
3003
+ if schedule_name is not None:
3004
+ body["scheduleName"] = schedule_name
3005
+ if schedule_type is not None:
3006
+ body["scheduleType"] = schedule_type
3007
+ if schedule_level is not None:
3008
+ body["scheduleLevel"] = schedule_level
3009
+ if calls_from is not None:
3010
+ body["callsFrom"] = calls_from
3011
+ if anonymous_callers_enabled is not None:
3012
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
3013
+ if unavailable_callers_enabled is not None:
3014
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
3015
+ if notification_enabled is not None:
3016
+ body["notificationEnabled"] = notification_enabled
3017
+ _missing = [f for f in ['scheduleName', 'scheduleType', 'scheduleLevel', 'callsFrom', 'notificationEnabled'] if f not in body or body[f] is None]
3018
+ if _missing:
3019
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
3020
+ raise typer.Exit(1)
3021
+ try:
3022
+ result = api.session.rest_post(url, json=body, params=params)
3023
+ except WebexError as e:
3024
+ handle_rest_error(e)
3025
+ if output == "json":
3026
+ print_json(result)
3027
+ elif isinstance(result, dict) and "id" in result:
3028
+ typer.echo(f"Created: {result['id']}")
3029
+ elif not result or result == {}:
3030
+ typer.echo("Created.")
3031
+ else:
3032
+ print_json(result)
3033
+
3034
+
3035
+
3036
+ @app.command("list-selective-forward")
3037
+ def list_selective_forward(
3038
+ workspace_id: str = typer.Argument(help="workspaceId"),
3039
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
3040
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
3041
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
3042
+ debug: bool = typer.Option(False, "--debug"),
3043
+ ):
3044
+ """Retrieve Selective Forward Settings for a Workspace."""
3045
+ api = get_api(debug=debug)
3046
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveForward"
3047
+ params = {}
3048
+ if limit > 0:
3049
+ params["max"] = limit
3050
+ if offset > 0:
3051
+ params["start"] = offset
3052
+ org_id = get_org_id()
3053
+ if org_id is not None:
3054
+ params["orgId"] = org_id
3055
+ result = None
3056
+ try:
3057
+ result = api.session.rest_get(url, params=params)
3058
+ except WebexError as e:
3059
+ handle_rest_error(e)
3060
+ result = result or []
3061
+ items = result.get("criteria", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
3062
+ if output == "json":
3063
+ print_json(items)
3064
+ else:
3065
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
3066
+
3067
+
3068
+
3069
+ @app.command("update-selective-forward")
3070
+ def update_selective_forward(
3071
+ workspace_id: str = typer.Argument(help="workspaceId"),
3072
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="`true` if the Selective Forward feature is enabled."),
3073
+ default_phone_number_to_forward: str = typer.Option(None, "--default-phone-number-to-forward", help="Enter the phone number to forward calls to during this sched"),
3074
+ ring_reminder_enabled: bool = typer.Option(None, "--ring-reminder-enabled/--no-ring-reminder-enabled", help="When `true`, enables a ring reminder for such calls."),
3075
+ destination_voicemail_enabled: bool = typer.Option(None, "--destination-voicemail-enabled/--no-destination-voicemail-enabled", help="Enables forwarding for all calls to voicemail. This option i"),
3076
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
3077
+ debug: bool = typer.Option(False, "--debug"),
3078
+ ):
3079
+ """Modify Selective Forward Settings for a Workspace."""
3080
+ api = get_api(debug=debug)
3081
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveForward"
3082
+ params = {}
3083
+ org_id = get_org_id()
3084
+ if org_id is not None:
3085
+ params["orgId"] = org_id
3086
+ if json_body:
3087
+ body = json.loads(json_body)
3088
+ else:
3089
+ body = {}
3090
+ if enabled is not None:
3091
+ body["enabled"] = enabled
3092
+ if default_phone_number_to_forward is not None:
3093
+ body["defaultPhoneNumberToForward"] = default_phone_number_to_forward
3094
+ if ring_reminder_enabled is not None:
3095
+ body["ringReminderEnabled"] = ring_reminder_enabled
3096
+ if destination_voicemail_enabled is not None:
3097
+ body["destinationVoicemailEnabled"] = destination_voicemail_enabled
3098
+ try:
3099
+ result = api.session.rest_put(url, json=body, params=params)
3100
+ except WebexError as e:
3101
+ handle_rest_error(e)
3102
+ typer.echo(f"Updated.")
3103
+
3104
+
3105
+
3106
+ @app.command("show-criteria-selective-forward")
3107
+ def show_criteria_selective_forward(
3108
+ workspace_id: str = typer.Argument(help="workspaceId"),
3109
+ id: str = typer.Argument(help="id"),
3110
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
3111
+ debug: bool = typer.Option(False, "--debug"),
3112
+ ):
3113
+ """Retrieve Selective Forward Criteria for a Workspace."""
3114
+ api = get_api(debug=debug)
3115
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveForward/criteria/{id}"
3116
+ params = {}
3117
+ org_id = get_org_id()
3118
+ if org_id is not None:
3119
+ params["orgId"] = org_id
3120
+ try:
3121
+ result = api.session.rest_get(url, params=params)
3122
+ except WebexError as e:
3123
+ handle_rest_error(e)
3124
+ if output == "json":
3125
+ print_json(result)
3126
+ else:
3127
+ if isinstance(result, dict):
3128
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
3129
+ elif isinstance(result, list):
3130
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
3131
+ else:
3132
+ print_json(result)
3133
+
3134
+
3135
+
3136
+ @app.command("update-criteria-selective-forward")
3137
+ def update_criteria_selective_forward(
3138
+ workspace_id: str = typer.Argument(help="workspaceId"),
3139
+ id: str = typer.Argument(help="id"),
3140
+ forward_to_phone_number: str = typer.Option(None, "--forward-to-phone-number", help="Phone number to forward calls to during this schedule."),
3141
+ destination_voicemail_enabled: bool = typer.Option(None, "--destination-voicemail-enabled/--no-destination-voicemail-enabled", help="Enables forwarding for all calls to voicemail. This option i"),
3142
+ schedule_name: str = typer.Option(None, "--schedule-name", help="Name of the location's schedule which determines when the se"),
3143
+ schedule_type: str = typer.Option(None, "--schedule-type", help="Choices: businessHours, holidays"),
3144
+ schedule_level: str = typer.Option(None, "--schedule-level", help="Choices: GROUP"),
3145
+ calls_from: str = typer.Option(None, "--calls-from", help="Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS, ANY_INTERNAL, ANY_EXTERNAL"),
3146
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables selective forward to calls from anonymo"),
3147
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables selective forward to calls if the calle"),
3148
+ forward_enabled: bool = typer.Option(None, "--forward-enabled/--no-forward-enabled", help="Indicates whether the calls, that fit within these parameter"),
3149
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
3150
+ debug: bool = typer.Option(False, "--debug"),
3151
+ ):
3152
+ """Modify Selective Forward Criteria for a Workspace\n\nExample --json-body:\n '{"callsFrom":"ANY_PHONE_NUMBER","forwardToPhoneNumber":"...","destinationVoicemailEnabled":true,"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","anonymousCallersEnabled":true,"unavailableCallersEnabled":true}'."""
3153
+ api = get_api(debug=debug)
3154
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveForward/criteria/{id}"
3155
+ params = {}
3156
+ org_id = get_org_id()
3157
+ if org_id is not None:
3158
+ params["orgId"] = org_id
3159
+ if json_body:
3160
+ body = json.loads(json_body)
3161
+ else:
3162
+ body = {}
3163
+ if forward_to_phone_number is not None:
3164
+ body["forwardToPhoneNumber"] = forward_to_phone_number
3165
+ if destination_voicemail_enabled is not None:
3166
+ body["destinationVoicemailEnabled"] = destination_voicemail_enabled
3167
+ if schedule_name is not None:
3168
+ body["scheduleName"] = schedule_name
3169
+ if schedule_type is not None:
3170
+ body["scheduleType"] = schedule_type
3171
+ if schedule_level is not None:
3172
+ body["scheduleLevel"] = schedule_level
3173
+ if calls_from is not None:
3174
+ body["callsFrom"] = calls_from
3175
+ if anonymous_callers_enabled is not None:
3176
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
3177
+ if unavailable_callers_enabled is not None:
3178
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
3179
+ if forward_enabled is not None:
3180
+ body["forwardEnabled"] = forward_enabled
3181
+ try:
3182
+ result = api.session.rest_put(url, json=body, params=params)
3183
+ except WebexError as e:
3184
+ handle_rest_error(e)
3185
+ typer.echo(f"Updated.")
3186
+
3187
+
3188
+
3189
+ @app.command("delete-criteria-selective-forward")
3190
+ def delete_criteria_selective_forward(
3191
+ workspace_id: str = typer.Argument(help="workspaceId"),
3192
+ id: str = typer.Argument(help="id"),
3193
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
3194
+ debug: bool = typer.Option(False, "--debug"),
3195
+ ):
3196
+ """Delete Selective Forward Criteria for a Workspace."""
3197
+ if not force:
3198
+ typer.confirm(f"Delete {id}?", abort=True)
3199
+ api = get_api(debug=debug)
3200
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveForward/criteria/{id}"
3201
+ params = {}
3202
+ org_id = get_org_id()
3203
+ if org_id is not None:
3204
+ params["orgId"] = org_id
3205
+ try:
3206
+ api.session.rest_delete(url, params=params)
3207
+ except WebexError as e:
3208
+ handle_rest_error(e)
3209
+ typer.echo(f"Deleted: {id}")
3210
+
3211
+
3212
+
3213
+ @app.command("create-criteria-selective-forward")
3214
+ def create_criteria_selective_forward(
3215
+ workspace_id: str = typer.Argument(help="workspaceId"),
3216
+ forward_to_phone_number: str = typer.Option(None, "--forward-to-phone-number", help="Phone number to forward calls to during this schedule."),
3217
+ destination_voicemail_enabled: bool = typer.Option(None, "--destination-voicemail-enabled/--no-destination-voicemail-enabled", help="Enables forwarding for all calls to voicemail. This option i"),
3218
+ schedule_name: str = typer.Option(None, "--schedule-name", help="Name of the location's schedule which determines when the se"),
3219
+ schedule_type: str = typer.Option(None, "--schedule-type", help="Choices: businessHours, holidays"),
3220
+ schedule_level: str = typer.Option(None, "--schedule-level", help="Choices: GROUP"),
3221
+ calls_from: str = typer.Option(None, "--calls-from", help="(required) Choices: ANY_PHONE_NUMBER, SELECT_PHONE_NUMBERS, ANY_INTERNAL, ANY_EXTERNAL"),
3222
+ anonymous_callers_enabled: bool = typer.Option(None, "--anonymous-callers-enabled/--no-anonymous-callers-enabled", help="When `true`, enables selective forward to calls from anonymo"),
3223
+ unavailable_callers_enabled: bool = typer.Option(None, "--unavailable-callers-enabled/--no-unavailable-callers-enabled", help="When `true`, enables selective forward to calls if the calle"),
3224
+ forward_enabled: bool = typer.Option(None, "--forward-enabled/--no-forward-enabled", help="Indicates whether the calls, that fit within these parameter"),
3225
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
3226
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
3227
+ debug: bool = typer.Option(False, "--debug"),
3228
+ ):
3229
+ """Create Selective Forward Criteria for a Workspace\n\nExample --json-body:\n '{"callsFrom":"ANY_PHONE_NUMBER","forwardToPhoneNumber":"...","destinationVoicemailEnabled":true,"scheduleName":"...","scheduleType":"businessHours","scheduleLevel":"GROUP","anonymousCallersEnabled":true,"unavailableCallersEnabled":true}'."""
3230
+ api = get_api(debug=debug)
3231
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/selectiveForward/criteria"
3232
+ params = {}
3233
+ org_id = get_org_id()
3234
+ if org_id is not None:
3235
+ params["orgId"] = org_id
3236
+ if json_body:
3237
+ body = json.loads(json_body)
3238
+ else:
3239
+ body = {}
3240
+ if forward_to_phone_number is not None:
3241
+ body["forwardToPhoneNumber"] = forward_to_phone_number
3242
+ if destination_voicemail_enabled is not None:
3243
+ body["destinationVoicemailEnabled"] = destination_voicemail_enabled
3244
+ if schedule_name is not None:
3245
+ body["scheduleName"] = schedule_name
3246
+ if schedule_type is not None:
3247
+ body["scheduleType"] = schedule_type
3248
+ if schedule_level is not None:
3249
+ body["scheduleLevel"] = schedule_level
3250
+ if calls_from is not None:
3251
+ body["callsFrom"] = calls_from
3252
+ if anonymous_callers_enabled is not None:
3253
+ body["anonymousCallersEnabled"] = anonymous_callers_enabled
3254
+ if unavailable_callers_enabled is not None:
3255
+ body["unavailableCallersEnabled"] = unavailable_callers_enabled
3256
+ if forward_enabled is not None:
3257
+ body["forwardEnabled"] = forward_enabled
3258
+ _missing = [f for f in ['callsFrom'] if f not in body or body[f] is None]
3259
+ if _missing:
3260
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
3261
+ raise typer.Exit(1)
3262
+ try:
3263
+ result = api.session.rest_post(url, json=body, params=params)
3264
+ except WebexError as e:
3265
+ handle_rest_error(e)
3266
+ if output == "json":
3267
+ print_json(result)
3268
+ elif isinstance(result, dict) and "id" in result:
3269
+ typer.echo(f"Created: {result['id']}")
3270
+ elif not result or result == {}:
3271
+ typer.echo("Created.")
3272
+ else:
3273
+ print_json(result)
3274
+
3275
+
3276
+
3277
+ @app.command("list-available-numbers-fax-message")
3278
+ def list_available_numbers_fax_message(
3279
+ workspace_id: str = typer.Argument(help="workspaceId"),
3280
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
3281
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
3282
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
3283
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
3284
+ debug: bool = typer.Option(False, "--debug"),
3285
+ ):
3286
+ """Get Workspace Fax Message Available Phone Numbers."""
3287
+ api = get_api(debug=debug)
3288
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/faxMessage/availableNumbers"
3289
+ params = {}
3290
+ if phone_number is not None:
3291
+ params["phoneNumber"] = phone_number
3292
+ if limit > 0:
3293
+ params["max"] = limit
3294
+ if offset > 0:
3295
+ params["start"] = offset
3296
+ org_id = get_org_id()
3297
+ if org_id is not None:
3298
+ params["orgId"] = org_id
3299
+ result = None
3300
+ try:
3301
+ result = api.session.rest_get(url, params=params)
3302
+ except WebexError as e:
3303
+ handle_rest_error(e)
3304
+ result = result or []
3305
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
3306
+ if output == "json":
3307
+ print_json(items)
3308
+ else:
3309
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
3310
+
3311
+
3312
+
3313
+ @app.command("list-available-numbers-secondary")
3314
+ def list_available_numbers_secondary(
3315
+ workspace_id: str = typer.Argument(help="workspaceId"),
3316
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
3317
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
3318
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
3319
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
3320
+ debug: bool = typer.Option(False, "--debug"),
3321
+ ):
3322
+ """Get Workspace Secondary Available Phone Numbers."""
3323
+ api = get_api(debug=debug)
3324
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/secondary/availableNumbers"
3325
+ params = {}
3326
+ if phone_number is not None:
3327
+ params["phoneNumber"] = phone_number
3328
+ if limit > 0:
3329
+ params["max"] = limit
3330
+ if offset > 0:
3331
+ params["start"] = offset
3332
+ org_id = get_org_id()
3333
+ if org_id is not None:
3334
+ params["orgId"] = org_id
3335
+ result = None
3336
+ try:
3337
+ result = api.session.rest_get(url, params=params)
3338
+ except WebexError as e:
3339
+ handle_rest_error(e)
3340
+ result = result or []
3341
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
3342
+ if output == "json":
3343
+ print_json(items)
3344
+ else:
3345
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
3346
+
3347
+