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,2541 @@
1
+ # Additional Call Features Reference
2
+
3
+ ## Sources
4
+
5
+ - wxc_sdk v1.30.0
6
+ - OpenAPI spec: specs/webex-cloud-calling.json
7
+ - developer.webex.com Call Features APIs
8
+
9
+ Comprehensive reference for Webex Calling Paging Groups, Call Park, Call Park Extensions, Call Pickup, Voicemail Groups, Customer Assist (formerly CX Essentials), Operating Modes, Call Recording, Announcements/Playlists, Single Number Reach, and Virtual Extensions using the `wxc_sdk`. Each section includes both SDK signatures and Raw HTTP examples.
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ 1. [SDK Access Paths](#sdk-access-paths)
16
+ 2. [Required Scopes](#required-scopes)
17
+ 3. [Paging Groups](#paging-groups)
18
+ 4. [Call Park](#call-park)
19
+ 5. [Call Park Extensions](#call-park-extensions)
20
+ 6. [Call Pickup](#call-pickup)
21
+ 7. [Voicemail Groups](#voicemail-groups)
22
+ 8. [Customer Assist (CX Essentials)](#customer-assist-cx-essentials)
23
+ 9. [Operating Modes](#operating-modes)
24
+ 10. [Call Recording (Org-Level)](#call-recording-org-level)
25
+ 11. [Announcements & Playlists](#announcements--playlists)
26
+ 12. [Single Number Reach](#single-number-reach)
27
+ 13. [Virtual Extensions](#virtual-extensions)
28
+ 14. [Data Models Quick Reference](#data-models-quick-reference)
29
+ 15. [Dependencies & Relationships](#dependencies--relationships)
30
+
31
+ ---
32
+
33
+ ## SDK Access Paths
34
+
35
+ All features in this document are accessed through the `TelephonyApi` instance:
36
+
37
+ ```python
38
+ from wxc_sdk import WebexSimpleApi
39
+
40
+ api = WebexSimpleApi(tokens='...')
41
+
42
+ api.telephony.paging # PagingApi
43
+ api.telephony.callpark # CallParkApi
44
+ api.telephony.callpark_extension # CallparkExtensionApi
45
+ api.telephony.callpickup # CallPickupApi
46
+ api.telephony.voicemail_groups # VoicemailGroupsApi
47
+ api.telephony.cx_essentials # CustomerExperienceEssentialsApi
48
+ ```
49
+
50
+ ---
51
+
52
+ ## Required Scopes
53
+
54
+ | Operation | Scope |
55
+ |-----------|-------|
56
+ | Read paging groups, call parks, call pickups, call park extensions, voicemail groups | `spark-admin:telephony_config_read` |
57
+ | Create/update/delete paging groups, call parks, call pickups, call park extensions, voicemail groups | `spark-admin:telephony_config_write` |
58
+ | Read Customer Assist queue recording | `spark-admin:people_read` |
59
+ | Configure Customer Assist queue recording | `spark-admin:people_write` |
60
+ | Read Customer Assist wrap-up reasons, screen pop, available agents | `spark-admin:telephony_config_read` |
61
+ | Modify Customer Assist wrap-up reasons, screen pop | `spark-admin:telephony_config_write` |
62
+
63
+ All APIs accept an optional `org_id` parameter, allowing partner administrators to operate on a customer organization.
64
+
65
+ ---
66
+
67
+ ## Paging Groups
68
+
69
+ ### Overview
70
+
71
+ Group Paging allows a person to place a **one-way call or group page** to up to **75 people and/or workspaces** by dialing a number or extension assigned to a specific paging group. The paging service makes a simultaneous call to all assigned targets.
72
+
73
+ Use cases: overhead announcements, warehouse pages, emergency notifications to a group of phones.
74
+
75
+ ### SDK API Class
76
+
77
+ ```python
78
+ class PagingApi(ApiChild, base='telephony/config')
79
+ ```
80
+
81
+ ### API Operations
82
+
83
+ #### List Paging Groups
84
+
85
+ Returns all paging groups across the organization (or filtered by location).
86
+
87
+ ```python
88
+ def list(
89
+ self,
90
+ location_id: str = None,
91
+ name: str = None,
92
+ phone_number: str = None,
93
+ org_id: str = None,
94
+ **params
95
+ ) -> Generator[Paging, None, None]
96
+ ```
97
+
98
+ - `location_id`: Filter to a specific location. Default is all locations.
99
+ - `name`: Filter by matching name.
100
+ - `phone_number`: Filter by matching primary phone number or extension.
101
+ - Returns paginated results with item key `locationPaging`.
102
+
103
+ #### Get Paging Group Details
104
+
105
+ ```python
106
+ def details(
107
+ self,
108
+ location_id: str,
109
+ paging_id: str,
110
+ org_id: str = None
111
+ ) -> Paging
112
+ ```
113
+
114
+ Returns the full `Paging` object including `originators` and `targets` with detailed agent info (first/last name, phone number, extension, type).
115
+
116
+ #### Create Paging Group
117
+
118
+ ```python
119
+ def create(
120
+ self,
121
+ location_id: str,
122
+ settings: Paging,
123
+ org_id: str = None
124
+ ) -> str # returns new paging group ID
125
+ ```
126
+
127
+ **Validation rule**: If `originators` are provided, `originator_caller_id_enabled` is **required** (raises `TypeError` otherwise).
128
+
129
+ Convenience factory:
130
+
131
+ ```python
132
+ settings = Paging.create(name='Warehouse Page', extension='8100')
133
+ paging_id = api.telephony.paging.create(location_id=loc_id, settings=settings)
134
+ ```
135
+
136
+ #### Update Paging Group
137
+
138
+ ```python
139
+ def update(
140
+ self,
141
+ location_id: str,
142
+ update: Paging,
143
+ paging_id: str,
144
+ org_id: str = None
145
+ ) -> None
146
+ ```
147
+
148
+ #### Delete Paging Group
149
+
150
+ ```python
151
+ def delete_paging(
152
+ self,
153
+ location_id: str,
154
+ paging_id: str,
155
+ org_id: str = None
156
+ ) -> None
157
+ ```
158
+
159
+ #### Get Available Phone Numbers
160
+
161
+ ```python
162
+ def primary_available_phone_numbers(
163
+ self,
164
+ location_id: str,
165
+ phone_number: List[str] = None,
166
+ org_id: str = None,
167
+ **params
168
+ ) -> Generator[AvailableNumber, None, None]
169
+ ```
170
+
171
+ Lists service and standard numbers available for assignment as the paging group's primary phone number. Numbers are associated with the specified location, can be active or inactive, and must be unassigned.
172
+
173
+ ### Key Data Models
174
+
175
+ #### `Paging`
176
+
177
+ | Field | Type | Required for Create | Notes |
178
+ |-------|------|-------------------|-------|
179
+ | `paging_id` | `str` (alias: `id`) | -- | Read-only, set by API |
180
+ | `enabled` | `bool` | No | Default varies |
181
+ | `name` | `str` | **Yes** | Max 30 chars |
182
+ | `phone_number` | `str` | **One of phone_number/extension** | Max 23 chars |
183
+ | `extension` | `str` | **One of phone_number/extension** | 2-10 chars |
184
+ | `toll_free_number` | `bool` | -- | Read-only |
185
+ | `language` | `str` | No | Read-only (use `language_code` for write) |
186
+ | `language_code` | `str` | No | e.g., `en_us` |
187
+ | `originator_caller_id_enabled` | `bool` | **Yes, if originators set** | Shows page originator ID on target caller ID |
188
+ | `originators` | `list[PagingAgent]` | No | People/workspaces who can originate pages |
189
+ | `targets` | `list[PagingAgent]` | No | People/workspaces/virtual lines who receive pages |
190
+ | `first_name` / `last_name` | `str` | No | **Deprecated** -- use `direct_line_caller_id_name` and `dial_by_name` |
191
+ | `direct_line_caller_id_name` | `DirectLineCallerIdName` | No | Replacement for first/last name |
192
+ | `dial_by_name` | `str` | No | Name used for dial-by-name directory |
193
+
194
+ #### `PagingAgent`
195
+
196
+ | Field | Type | Notes |
197
+ |-------|------|-------|
198
+ | `agent_id` (alias: `id`) | `str` | **Only field sent in create/update** |
199
+ | `first_name` | `str` | Read-only (returned from details) |
200
+ | `last_name` | `str` | Read-only |
201
+ | `agent_type` (alias: `type`) | `UserType` | `PEOPLE` or `PLACE` |
202
+ | `phone_number` | `str` | Read-only |
203
+ | `extension` | `str` | Read-only |
204
+ | `routing_prefix` | `str` | Read-only |
205
+ | `esn` | `str` | Read-only (routing prefix + extension) |
206
+
207
+ **Important**: When creating or updating, `originators` and `targets` are serialized as flat lists of IDs only, not full agent objects.
208
+
209
+ ### Phone Number / Extension Assignment
210
+
211
+ - Either `phone_number` or `extension` is mandatory (enforced by `Paging.create()` factory).
212
+ - Use `primary_available_phone_numbers()` to find unassigned numbers at a location.
213
+ - The `toll_free_number` flag is read-only and reflects whether the assigned number is toll-free.
214
+
215
+ ### Member Management
216
+
217
+ - **Originators**: People/workspaces who can initiate a page. Set via `originators` field as a list of `PagingAgent` with `agent_id` populated.
218
+ - **Targets**: People/workspaces/virtual lines who receive the page broadcast. Set via `targets` field. Up to 75 targets.
219
+ - On create/update, pass agent IDs only. On read (details), full agent info is returned.
220
+
221
+ ### Raw HTTP
222
+
223
+ <!-- Updated by playbook session 2026-03-18 -->
224
+
225
+ ```python
226
+ from wxc_sdk import WebexSimpleApi
227
+ api = WebexSimpleApi()
228
+ BASE = "https://webexapis.com/v1"
229
+ ```
230
+
231
+ **List Paging Groups** (org-wide, filterable by location):
232
+
233
+ ```python
234
+ result = api.session.rest_get(f"{BASE}/telephony/config/paging",
235
+ params={"locationId": loc_id, "name": "Warehouse", "max": 1000})
236
+ items = result["paging"] # response key: "paging"
237
+ # Each item: {id, name, extension, phoneNumber, enabled, locationId, locationName, routingPrefix, esn, tollFreeNumber}
238
+ ```
239
+
240
+ **Get Paging Group Details:**
241
+
242
+ ```python
243
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/paging/{paging_id}")
244
+ # Returns full object including originators[], targets[] with firstName, lastName, type, phoneNumber, extension
245
+ ```
246
+
247
+ **Create Paging Group** -- required: `name`, one of `phoneNumber`/`extension`:
248
+
249
+ ```python
250
+ body = {
251
+ "name": "Warehouse Page",
252
+ "extension": "8100",
253
+ "languageCode": "en_us",
254
+ "originatorCallerIdEnabled": True, # required if originators are set
255
+ "originators": [person_id], # Plain string array, NOT [{"id": person_id}]
256
+ "targets": [person_id, workspace_id] # Plain string array, NOT [{"id": ...}]
257
+ }
258
+ # IMPORTANT: targets and originators are arrays of ID strings, not objects.
259
+ # [{"id": ...}] returns 400 "Invalid field value". Verified via stress test 2026-03-25.
260
+ result = api.session.rest_post(f"{BASE}/telephony/config/locations/{loc_id}/paging", json=body)
261
+ paging_id = result["id"]
262
+ ```
263
+
264
+ **Update Paging Group:**
265
+
266
+ ```python
267
+ api.session.rest_put(f"{BASE}/telephony/config/locations/{loc_id}/paging/{paging_id}",
268
+ json={"name": "Updated Name", "enabled": True})
269
+ ```
270
+
271
+ **Delete Paging Group:**
272
+
273
+ ```python
274
+ api.session.rest_delete(f"{BASE}/telephony/config/locations/{loc_id}/paging/{paging_id}")
275
+ ```
276
+
277
+ **Get Available Phone Numbers:**
278
+
279
+ ```python
280
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/paging/availableNumbers",
281
+ params={"max": 1000})
282
+ items = result["availableNumbers"]
283
+ ```
284
+
285
+ ### CLI Examples
286
+
287
+ ```bash
288
+ # List all paging groups (org-wide)
289
+ wxcli paging-group list
290
+
291
+ # List paging groups filtered by location
292
+ wxcli paging-group list --location-id <loc_id>
293
+
294
+ # Get details for a specific paging group
295
+ wxcli paging-group show --location-id <loc_id> --paging-group-id <pg_id>
296
+
297
+ # Create a paging group
298
+ wxcli paging-group create --location-id <loc_id> --name "Warehouse Page" --extension "8100"
299
+
300
+ # Update a paging group
301
+ wxcli paging-group update --location-id <loc_id> --paging-group-id <pg_id> --name "Updated Page"
302
+
303
+ # Delete a paging group
304
+ wxcli paging-group delete --location-id <loc_id> --paging-group-id <pg_id>
305
+
306
+ # List available phone numbers for paging group assignment
307
+ wxcli paging-group list-available-numbers --location-id <loc_id>
308
+ ```
309
+
310
+ ---
311
+
312
+ ## Call Park
313
+
314
+ ### Overview
315
+
316
+ Call Park allows a call recipient to place a call **on hold at a designated park location** so it can be retrieved from another device. Users park calls against extensions or Call Park Extensions, then other users retrieve them by dialing the park extension.
317
+
318
+ Use cases: front-desk parks a call, employee picks it up from their desk phone; warehouse environments where users move between stations.
319
+
320
+ ### SDK API Class
321
+
322
+ ```python
323
+ class CallParkApi(ApiChild, base='telephony/config/callParks')
324
+ ```
325
+
326
+ ### API Operations
327
+
328
+ #### List Call Parks
329
+
330
+ ```python
331
+ def list(
332
+ self,
333
+ location_id: str,
334
+ order: str = None, # 'ASC' or 'DSC'
335
+ name: str = None, # filter by name (max 80 chars)
336
+ org_id: str = None,
337
+ **params
338
+ ) -> Generator[CallPark, None, None]
339
+ ```
340
+
341
+ **NOTE**: The Call Park ID will change upon modification of the Call Park name.
342
+
343
+ #### Get Call Park Details
344
+
345
+ ```python
346
+ def details(
347
+ self,
348
+ location_id: str,
349
+ callpark_id: str,
350
+ org_id: str = None
351
+ ) -> CallPark
352
+ ```
353
+
354
+ #### Create Call Park
355
+
356
+ ```python
357
+ def create(
358
+ self,
359
+ location_id: str,
360
+ settings: CallPark,
361
+ org_id: str = None
362
+ ) -> str # returns new call park ID
363
+ ```
364
+
365
+ Convenience factory (minimal call park with recall to parking user only):
366
+
367
+ ```python
368
+ settings = CallPark.default(name='Lobby Park')
369
+ park_id = api.telephony.callpark.create(location_id=loc_id, settings=settings)
370
+ ```
371
+
372
+ #### Update Call Park
373
+
374
+ ```python
375
+ def update(
376
+ self,
377
+ location_id: str,
378
+ callpark_id: str,
379
+ settings: CallPark,
380
+ org_id: str = None
381
+ ) -> str # returns updated call park ID
382
+ ```
383
+
384
+ **NOTE**: The Call Park ID changes when the name is modified. The returned ID is the new one.
385
+
386
+ #### Delete Call Park
387
+
388
+ ```python
389
+ def delete_callpark(
390
+ self,
391
+ location_id: str,
392
+ callpark_id: str,
393
+ org_id: str = None
394
+ ) -> None
395
+ ```
396
+
397
+ #### Get Available Agents
398
+
399
+ ```python
400
+ def available_agents(
401
+ self,
402
+ location_id: str,
403
+ call_park_name: str = None,
404
+ name: str = None,
405
+ phone_number: str = None,
406
+ order: str = None,
407
+ org_id: str = None,
408
+ **params
409
+ ) -> Generator[PersonPlaceAgent, None, None]
410
+ ```
411
+
412
+ Returns people and workspaces eligible to be added as call park agents. Sort fields: `fname`, `lname`, `number`, `extension` (pipe-separated, max 3).
413
+
414
+ #### Get Available Recall Hunt Groups
415
+
416
+ ```python
417
+ def available_recalls(
418
+ self,
419
+ location_id: str,
420
+ name: str = None,
421
+ order: str = None,
422
+ org_id: str = None,
423
+ **params
424
+ ) -> Generator[AvailableRecallHuntGroup, None, None]
425
+ ```
426
+
427
+ Lists hunt groups that can be used as recall destinations.
428
+
429
+ #### Get Location Call Park Settings
430
+
431
+ ```python
432
+ def call_park_settings(
433
+ self,
434
+ location_id: str,
435
+ org_id: str = None
436
+ ) -> LocationCallParkSettings
437
+ ```
438
+
439
+ #### Update Location Call Park Settings
440
+
441
+ ```python
442
+ def update_call_park_settings(
443
+ self,
444
+ location_id: str,
445
+ settings: LocationCallParkSettings,
446
+ org_id: str = None
447
+ ) -> None
448
+ ```
449
+
450
+ ### Key Data Models
451
+
452
+ #### `CallPark`
453
+
454
+ | Field | Type | Notes |
455
+ |-------|------|-------|
456
+ | `callpark_id` (alias: `id`) | `str` | Read-only. **Changes when name is modified.** |
457
+ | `name` | `str` | Max 80 chars |
458
+ | `location_name` | `str` | Read-only |
459
+ | `location_id` | `str` | Read-only |
460
+ | `recall` | `RecallHuntGroup` | Recall options (who gets alerted when park times out) |
461
+ | `agents` | `list[PersonPlaceAgent]` | People/workspaces eligible to receive parked calls |
462
+ | `park_on_agents_enabled` | `bool` | Whether calls can be parked on agents as a destination |
463
+ | `call_park_extensions` | `list[CallParkExtension]` | Park extensions assigned to this call park |
464
+
465
+ **Important**: On create/update, `agents` and `call_park_extensions` are serialized as flat lists of IDs only.
466
+
467
+ #### `RecallHuntGroup`
468
+
469
+ | Field | Type | Notes |
470
+ |-------|------|-------|
471
+ | `hunt_group_id` | `str` | Hunt group ID for recall alternate destination |
472
+ | `hunt_group_name` | `str` | Read-only (excluded from create/update) |
473
+ | `option` | `CallParkRecall` | Recall behavior enum |
474
+
475
+ #### `CallParkRecall` (Enum)
476
+
477
+ | Value | Constant | Description |
478
+ |-------|----------|-------------|
479
+ | `ALERT_PARKING_USER_ONLY` | `parking_user_only` | Alert only the user who parked the call |
480
+ | `ALERT_PARKING_USER_FIRST_THEN_HUNT_GROUP` | `parking_user_first_then_hunt_group` | Alert parking user first, then hunt group |
481
+ | `ALERT_HUNT_GROUP_ONLY` | `hunt_group_only` | Alert hunt group only |
482
+
483
+ #### `CallParkSettings` (Location-Level)
484
+
485
+ | Field | Type | Default | Notes |
486
+ |-------|------|---------|-------|
487
+ | `ring_pattern` | `RingPattern` | `normal` | Ring pattern when park extension is called |
488
+ | `recall_time` | `int` | `45` | Seconds before recall (30-600 range) |
489
+ | `hunt_wait_time` | `int` | `45` | Seconds before reverting to hunt group (30-600 range) |
490
+
491
+ #### `LocationCallParkSettings`
492
+
493
+ Wraps `call_park_recall` (`RecallHuntGroup`) and `call_park_settings` (`CallParkSettings`) into a single object for location-level configuration.
494
+
495
+ ### Phone Number / Extension Assignment
496
+
497
+ Call Parks themselves do not have phone numbers or extensions directly. They are groups of agents and park extensions. Calls are parked **to** a Call Park Extension (see next section) or to an agent's extension.
498
+
499
+ ### Member Management
500
+
501
+ - **Agents**: People and workspaces eligible to receive parked calls. Added via the `agents` field (list of IDs on create/update).
502
+ - **Call Park Extensions**: Dedicated extensions for parking. Added via `call_park_extensions` field (list of IDs on create/update).
503
+ - Use `available_agents()` to discover eligible agents at a location.
504
+ - Use `available_recalls()` to discover hunt groups eligible as recall destinations.
505
+
506
+ ### Raw HTTP
507
+
508
+ <!-- Updated by playbook session 2026-03-18 -->
509
+
510
+ ```python
511
+ from wxc_sdk import WebexSimpleApi
512
+ api = WebexSimpleApi()
513
+ BASE = "https://webexapis.com/v1"
514
+ ```
515
+
516
+ **List Call Parks** (location-scoped):
517
+
518
+ ```python
519
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callParks",
520
+ params={"name": "Lobby", "order": "ASC", "max": 1000})
521
+ items = result["callParks"] # response key: "callParks"
522
+ # Each item: {id, name}
523
+ ```
524
+
525
+ **Get Call Park Details:**
526
+
527
+ ```python
528
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callParks/{park_id}")
529
+ # Returns: name, recall, agents[], callParkExtensions[], parkOnAgentsEnabled
530
+ ```
531
+
532
+ **Create Call Park** -- required: `name`, `recall` with `option`:
533
+
534
+ ```python
535
+ body = {
536
+ "name": "Lobby Park",
537
+ "recall": {"option": "ALERT_PARKING_USER_ONLY"}, # required
538
+ "parkOnAgentsEnabled": True,
539
+ "agents": [{"id": person_id}],
540
+ "callParkExtensions": [{"id": cpe_id}]
541
+ }
542
+ result = api.session.rest_post(f"{BASE}/telephony/config/locations/{loc_id}/callParks", json=body)
543
+ park_id = result["id"]
544
+ ```
545
+
546
+ **Update Call Park** (ID changes when name changes):
547
+
548
+ ```python
549
+ result = api.session.rest_put(f"{BASE}/telephony/config/locations/{loc_id}/callParks/{park_id}",
550
+ json={"name": "New Name", "parkOnAgentsEnabled": False})
551
+ # New park_id returned if name changed
552
+ ```
553
+
554
+ **Delete Call Park:**
555
+
556
+ ```python
557
+ api.session.rest_delete(f"{BASE}/telephony/config/locations/{loc_id}/callParks/{park_id}")
558
+ ```
559
+
560
+ **Get Available Agents:**
561
+
562
+ ```python
563
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callParks/availableUsers",
564
+ params={"name": "John", "max": 1000})
565
+ items = result["availableUsers"] # response key: "availableUsers"
566
+ ```
567
+
568
+ **Get Available Recall Hunt Groups:**
569
+
570
+ ```python
571
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callParks/availableRecallHuntGroups",
572
+ params={"max": 1000})
573
+ items = result["availableRecallHuntGroups"]
574
+ ```
575
+
576
+ **Get Location Call Park Settings:**
577
+
578
+ ```python
579
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callParks/settings")
580
+ # Returns: callParkRecall, callParkSettings (ringPattern, recallTime, huntWaitTime)
581
+ ```
582
+
583
+ **Update Location Call Park Settings:**
584
+
585
+ ```python
586
+ api.session.rest_put(f"{BASE}/telephony/config/locations/{loc_id}/callParks/settings",
587
+ json={"callParkSettings": {"recallTime": 60, "huntWaitTime": 60}})
588
+ ```
589
+
590
+ ### CLI Examples
591
+
592
+ ```bash
593
+ # List call parks at a location
594
+ wxcli call-park list --location-id <loc_id>
595
+
596
+ # Get details for a call park
597
+ wxcli call-park show --location-id <loc_id> --call-park-id <park_id>
598
+
599
+ # Create a call park
600
+ wxcli call-park create --location-id <loc_id> --name "Lobby Park" --json-body '{"recall": {"option": "ALERT_PARKING_USER_ONLY"}}'
601
+
602
+ # Update a call park
603
+ wxcli call-park update --location-id <loc_id> --call-park-id <park_id> --name "New Park Name"
604
+
605
+ # Delete a call park
606
+ wxcli call-park delete --location-id <loc_id> --call-park-id <park_id>
607
+
608
+ # List available agents for call park
609
+ wxcli call-park list-available-users --location-id <loc_id>
610
+
611
+ # List available recall hunt groups
612
+ wxcli call-park list-available-recall-hunt-groups --location-id <loc_id>
613
+
614
+ # Get location-level call park settings
615
+ wxcli call-park show-settings --location-id <loc_id>
616
+
617
+ # Update location-level call park settings
618
+ wxcli call-park update-settings --location-id <loc_id> --json-body '{"callParkSettings": {"recallTime": 60}}'
619
+ ```
620
+
621
+ ---
622
+
623
+ ## Call Park Extensions
624
+
625
+ ### Overview
626
+
627
+ Call Park Extensions are **dedicated extensions defined within the Call Park service** for holding parked calls. A user parks a call to one of these extensions, and another user retrieves it by dialing that extension. Call Park Extensions can also be added as **monitored lines** on Cisco phones, so users can park and retrieve calls by pressing a line key.
628
+
629
+ The Call Park service is enabled for all users by default.
630
+
631
+ ### SDK API Class
632
+
633
+ ```python
634
+ class CallparkExtensionApi(ApiChild, base='telephony')
635
+ ```
636
+
637
+ ### API Operations
638
+
639
+ #### List Call Park Extensions
640
+
641
+ ```python
642
+ def list(
643
+ self,
644
+ extension: str = None,
645
+ name: str = None,
646
+ location_id: str = None,
647
+ location_name: str = None,
648
+ order: str = None,
649
+ org_id: str = None,
650
+ **params
651
+ ) -> Generator[CallParkExtension, None, None]
652
+ ```
653
+
654
+ Lists across all locations if `location_id` is not specified. Sort fields: `groupName`, `callParkExtension`, `callParkExtensionName`, `callParkExtensionExternalId`.
655
+
656
+ #### Get Call Park Extension Details
657
+
658
+ ```python
659
+ def details(
660
+ self,
661
+ location_id: str,
662
+ cpe_id: str,
663
+ org_id: str = None
664
+ ) -> CallParkExtension
665
+ ```
666
+
667
+ Returns `CallParkExtension` with `name` and `extension` fields.
668
+
669
+ #### Create Call Park Extension
670
+
671
+ ```python
672
+ def create(
673
+ self,
674
+ location_id: str,
675
+ name: str, # max 30 chars
676
+ extension: str, # 2-10 chars, must be unique
677
+ org_id: str = None
678
+ ) -> str # returns new call park extension ID
679
+ ```
680
+
681
+ #### Update Call Park Extension
682
+
683
+ ```python
684
+ def update(
685
+ self,
686
+ location_id: str,
687
+ cpe_id: str,
688
+ name: str = None,
689
+ extension: str = None,
690
+ org_id: str = None
691
+ ) -> None
692
+ ```
693
+
694
+ Both `name` and `extension` are optional -- only the provided fields are updated.
695
+
696
+ #### Delete Call Park Extension
697
+
698
+ ```python
699
+ def delete(
700
+ self,
701
+ location_id: str,
702
+ cpe_id: str,
703
+ org_id: str = None
704
+ ) -> None
705
+ ```
706
+
707
+ ### Key Data Models
708
+
709
+ #### `CallParkExtension` (from `wxc_sdk.common`)
710
+
711
+ | Field | Type | Notes |
712
+ |-------|------|-------|
713
+ | `cpe_id` (alias: `id`) | `str` | Unique identifier |
714
+ | `name` | `str` | Max 30 chars |
715
+ | `extension` | `str` | 2-10 chars, must be unique within location |
716
+
717
+ This is a lightweight model -- just a name and extension. The `CallParkExtension` model is shared from `wxc_sdk.common` and also used within the `CallPark` model to list assigned park extensions.
718
+
719
+ ### Phone Number / Extension Assignment
720
+
721
+ - Each Call Park Extension has a **unique extension** within its location.
722
+ - No phone number assignment -- these are extension-only entities.
723
+ - Extensions are dialed internally to park or retrieve calls.
724
+
725
+ ### Member Management
726
+
727
+ Call Park Extensions do not have members. They are simple extension holders assigned to Call Park groups (see the `call_park_extensions` field on `CallPark`).
728
+
729
+ ### Raw HTTP
730
+
731
+ <!-- Updated by playbook session 2026-03-18 -->
732
+
733
+ ```python
734
+ from wxc_sdk import WebexSimpleApi
735
+ api = WebexSimpleApi()
736
+ BASE = "https://webexapis.com/v1"
737
+ ```
738
+
739
+ **List Call Park Extensions** (org-wide, filterable by location):
740
+
741
+ ```python
742
+ result = api.session.rest_get(f"{BASE}/telephony/config/callParkExtensions",
743
+ params={"locationId": loc_id, "name": "Park", "max": 1000})
744
+ items = result["callParkExtensions"] # response key: "callParkExtensions"
745
+ # Each item: {id, name, extension, locationId, locationName}
746
+ ```
747
+
748
+ **Get Call Park Extension Details:**
749
+
750
+ ```python
751
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callParkExtensions/{cpe_id}")
752
+ # Returns: name, extension
753
+ ```
754
+
755
+ **Create Call Park Extension** -- required: `name`, `extension`:
756
+
757
+ ```python
758
+ result = api.session.rest_post(f"{BASE}/telephony/config/locations/{loc_id}/callParkExtensions",
759
+ json={"name": "Park Slot 1", "extension": "7001"})
760
+ cpe_id = result["id"]
761
+ ```
762
+
763
+ **Update Call Park Extension:**
764
+
765
+ ```python
766
+ api.session.rest_put(f"{BASE}/telephony/config/locations/{loc_id}/callParkExtensions/{cpe_id}",
767
+ json={"name": "Park Slot A", "extension": "7002"})
768
+ ```
769
+
770
+ **Delete Call Park Extension:**
771
+
772
+ ```python
773
+ api.session.rest_delete(f"{BASE}/telephony/config/locations/{loc_id}/callParkExtensions/{cpe_id}")
774
+ ```
775
+
776
+ ### CLI Examples
777
+
778
+ ```bash
779
+ # List call park extensions
780
+ wxcli call-park list-call-park-extensions --location-id <loc_id>
781
+
782
+ # Get details for a call park extension
783
+ wxcli call-park show-call-park-extensions --location-id <loc_id> --call-park-extension-id <cpe_id>
784
+
785
+ # Create a call park extension
786
+ wxcli call-park create-call-park-extensions --location-id <loc_id> --name "Park Slot 1" --extension "7001"
787
+
788
+ # Update a call park extension
789
+ wxcli call-park update-call-park-extensions --location-id <loc_id> --call-park-extension-id <cpe_id> --name "Park Slot A"
790
+
791
+ # Delete a call park extension
792
+ wxcli call-park delete-call-park-extensions --location-id <loc_id> --call-park-extension-id <cpe_id>
793
+ ```
794
+
795
+ ---
796
+
797
+ ## Call Pickup
798
+
799
+ ### Overview
800
+
801
+ Call Pickup enables a user (agent) to **answer any ringing line within their pickup group**. When a call rings unanswered for a configurable period, all members of the pickup group can be notified via audio, visual, or both.
802
+
803
+ Use cases: small offices where team members cover each other's calls; receptionist groups.
804
+
805
+ ### SDK API Class
806
+
807
+ ```python
808
+ class CallPickupApi(ApiChild, base='telephony/config/callPickups')
809
+ ```
810
+
811
+ ### API Operations
812
+
813
+ #### List Call Pickups
814
+
815
+ ```python
816
+ def list(
817
+ self,
818
+ location_id: str,
819
+ order: Literal['ASC', 'DSC'] = None,
820
+ name: str = None,
821
+ org_id: str = None,
822
+ **params
823
+ ) -> Generator[CallPickup, None, None]
824
+ ```
825
+
826
+ **NOTE**: The Call Pickup ID will change upon modification of the Call Pickup name.
827
+
828
+ #### Get Call Pickup Details
829
+
830
+ ```python
831
+ def details(
832
+ self,
833
+ location_id: str,
834
+ pickup_id: str,
835
+ org_id: str = None
836
+ ) -> CallPickup
837
+ ```
838
+
839
+ #### Create Call Pickup
840
+
841
+ ```python
842
+ def create(
843
+ self,
844
+ location_id: str,
845
+ settings: CallPickup,
846
+ org_id: str = None
847
+ ) -> str # returns new call pickup ID
848
+ ```
849
+
850
+ #### Update Call Pickup
851
+
852
+ ```python
853
+ def update(
854
+ self,
855
+ location_id: str,
856
+ pickup_id: str,
857
+ settings: CallPickup,
858
+ org_id: str = None
859
+ ) -> str # returns updated call pickup ID
860
+ ```
861
+
862
+ **NOTE**: The Call Pickup ID changes when the name is modified.
863
+
864
+ #### Delete Call Pickup
865
+
866
+ ```python
867
+ def delete_pickup(
868
+ self,
869
+ location_id: str,
870
+ pickup_id: str,
871
+ org_id: str = None
872
+ ) -> None
873
+ ```
874
+
875
+ #### Get Available Agents
876
+
877
+ ```python
878
+ def available_agents(
879
+ self,
880
+ location_id: str,
881
+ call_pickup_name: str = None,
882
+ name: str = None,
883
+ phone_number: str = None,
884
+ order: str = None,
885
+ org_id: str = None,
886
+ **params
887
+ ) -> Generator[PersonPlaceAgent, None, None]
888
+ ```
889
+
890
+ Returns people, workspaces, and virtual lines eligible to be added to pickup groups. Sort fields: `fname`, `lname`, `number`, `extension` (pipe-separated, max 3).
891
+
892
+ ### Key Data Models
893
+
894
+ #### `CallPickup`
895
+
896
+ | Field | Type | Notes |
897
+ |-------|------|-------|
898
+ | `pickup_id` (alias: `id`) | `str` | Read-only. **Changes when name is modified.** |
899
+ | `name` | `str` | Max 80 chars |
900
+ | `location_name` | `str` | Read-only |
901
+ | `location_id` | `str` | Read-only |
902
+ | `notification_type` | `PickupNotificationType` | How members are notified of unanswered calls |
903
+ | `notification_delay_timer_seconds` | `int` | Seconds before notification fires |
904
+ | `agents` | `list[PersonPlaceAgent]` | People, workspaces, virtual lines in the group |
905
+
906
+ **Important**: On create/update, `agents` is serialized as a flat list of IDs only.
907
+
908
+ #### `PickupNotificationType` (Enum)
909
+
910
+ | Value | Constant | Description |
911
+ |-------|----------|-------------|
912
+ | `NONE` | `none` | No notification sent |
913
+ | `AUDIO_ONLY` | `audio_only` | Audio notification after delay |
914
+ | `VISUAL_ONLY` | `visual_only` | Visual notification after delay |
915
+ | `AUDIO_AND_VISUAL` | `audio_and_visual` | Both audio and visual after delay |
916
+
917
+ ### Phone Number / Extension Assignment
918
+
919
+ Call Pickup groups do not have their own phone numbers or extensions. They are groupings of agents -- members pick up ringing calls within the group using the feature access code `*98`.
920
+
921
+ ### Member Management
922
+
923
+ - **Agents**: People, workspaces, and virtual lines. Set via the `agents` field (list of IDs on create/update).
924
+ - Use `available_agents()` to discover eligible agents at a location that are not yet assigned to another pickup group. The API excludes already-assigned agents from the results.
925
+ - A user can only belong to **one** call pickup group at a time. Attempting to add an already-assigned user returns error 4471: "User ... is already assigned to pickup group ...".
926
+
927
+ ### Raw HTTP
928
+
929
+ <!-- Updated by playbook session 2026-03-18 -->
930
+
931
+ ```python
932
+ from wxc_sdk import WebexSimpleApi
933
+ api = WebexSimpleApi()
934
+ BASE = "https://webexapis.com/v1"
935
+ ```
936
+
937
+ **List Call Pickups** (location-scoped):
938
+
939
+ ```python
940
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callPickups",
941
+ params={"name": "Sales", "order": "ASC", "max": 1000})
942
+ items = result["callPickups"] # response key: "callPickups"
943
+ # Each item: {id, name}
944
+ ```
945
+
946
+ **Get Call Pickup Details:**
947
+
948
+ ```python
949
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callPickups/{pickup_id}")
950
+ # Returns: name, notificationType, notificationDelayTimerSeconds, agents[]
951
+ ```
952
+
953
+ **Create Call Pickup** -- required: `name`:
954
+
955
+ ```python
956
+ body = {
957
+ "name": "Sales Pickup",
958
+ "notificationType": "AUDIO_AND_VISUAL",
959
+ "notificationDelayTimerSeconds": 6,
960
+ "agents": [person_id] # Plain string array, NOT [{"id": person_id}]
961
+ }
962
+ # IMPORTANT: agents is an array of ID strings, not objects. [{"id": ...}] returns 400.
963
+ # Verified via stress test 2026-03-25.
964
+ result = api.session.rest_post(f"{BASE}/telephony/config/locations/{loc_id}/callPickups", json=body)
965
+ pickup_id = result["id"]
966
+ ```
967
+
968
+ **Update Call Pickup** (ID changes when name changes):
969
+
970
+ ```python
971
+ api.session.rest_put(f"{BASE}/telephony/config/locations/{loc_id}/callPickups/{pickup_id}",
972
+ json={"name": "New Name", "notificationType": "NONE"})
973
+ ```
974
+
975
+ **Delete Call Pickup:**
976
+
977
+ ```python
978
+ api.session.rest_delete(f"{BASE}/telephony/config/locations/{loc_id}/callPickups/{pickup_id}")
979
+ ```
980
+
981
+ **Get Available Agents:**
982
+
983
+ ```python
984
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/callPickups/availableUsers",
985
+ params={"name": "Jane", "max": 1000})
986
+ items = result["availableUsers"] # response key: "availableUsers"
987
+ ```
988
+
989
+ ### CLI Examples
990
+
991
+ ```bash
992
+ # List call pickups at a location
993
+ wxcli call-pickup list --location-id <loc_id>
994
+
995
+ # Get details for a call pickup group
996
+ wxcli call-pickup show --location-id <loc_id> --call-pickup-id <pickup_id>
997
+
998
+ # Create a call pickup group
999
+ wxcli call-pickup create --location-id <loc_id> --name "Sales Pickup"
1000
+
1001
+ # Update a call pickup group
1002
+ wxcli call-pickup update --location-id <loc_id> --call-pickup-id <pickup_id> --name "New Name"
1003
+
1004
+ # Delete a call pickup group
1005
+ wxcli call-pickup delete --location-id <loc_id> --call-pickup-id <pickup_id>
1006
+
1007
+ # List available agents for call pickup
1008
+ wxcli call-pickup list-available-users --location-id <loc_id>
1009
+ ```
1010
+
1011
+ ---
1012
+
1013
+ ## Voicemail Groups
1014
+
1015
+ ### Overview
1016
+
1017
+ Voicemail Groups provide a **shared voicemail box** (and optional inbound fax box) that can be assigned to users or call routing features like auto attendants, call queues, or hunt groups. They are useful for team-level voicemail where multiple people need access to the same messages.
1018
+
1019
+ ### SDK API Class
1020
+
1021
+ ```python
1022
+ class VoicemailGroupsApi(ApiChild, base='telephony/config/voicemailGroups')
1023
+ ```
1024
+
1025
+ ### API Operations
1026
+
1027
+ #### List Voicemail Groups
1028
+
1029
+ ```python
1030
+ def list(
1031
+ self,
1032
+ location_id: str = None,
1033
+ name: str = None,
1034
+ phone_number: str = None,
1035
+ org_id: str = None,
1036
+ **params
1037
+ ) -> Generator[VoicemailGroup, None, None]
1038
+ ```
1039
+
1040
+ - `name`: Search (contains) based on voicemail group name.
1041
+ - `phone_number`: Search (contains) based on number or extension.
1042
+
1043
+ #### Get Voicemail Group Details
1044
+
1045
+ ```python
1046
+ def details(
1047
+ self,
1048
+ location_id: str,
1049
+ voicemail_group_id: str,
1050
+ org_id: str = None
1051
+ ) -> VoicemailGroupDetail
1052
+ ```
1053
+
1054
+ #### Create Voicemail Group
1055
+
1056
+ ```python
1057
+ def create(
1058
+ self,
1059
+ location_id: str,
1060
+ settings: VoicemailGroupDetail,
1061
+ org_id: str = None
1062
+ ) -> str # returns new voicemail group ID
1063
+ ```
1064
+
1065
+ Convenience factory with sensible defaults (internal storage, all notifications disabled):
1066
+
1067
+ ```python
1068
+ settings = VoicemailGroupDetail.create(
1069
+ name='Support VM',
1070
+ extension='8200',
1071
+ first_name='Support',
1072
+ last_name='Team',
1073
+ passcode=740384,
1074
+ language_code='en_us',
1075
+ phone_number='+14155551234' # optional
1076
+ )
1077
+ vmg_id = api.telephony.voicemail_groups.create(
1078
+ location_id=loc_id, settings=settings
1079
+ )
1080
+ ```
1081
+
1082
+ #### Update Voicemail Group
1083
+
1084
+ ```python
1085
+ def update(
1086
+ self,
1087
+ location_id: str,
1088
+ voicemail_group_id: str,
1089
+ settings: VoicemailGroupDetail,
1090
+ org_id: str = None
1091
+ ) -> None
1092
+ ```
1093
+
1094
+ #### Delete Voicemail Group
1095
+
1096
+ ```python
1097
+ def delete(
1098
+ self,
1099
+ location_id: str,
1100
+ voicemail_group_id: str,
1101
+ org_id: str = None
1102
+ ) -> None
1103
+ ```
1104
+
1105
+ #### Get Available Phone Numbers
1106
+
1107
+ ```python
1108
+ def available_phone_numbers(
1109
+ self,
1110
+ location_id: str,
1111
+ phone_number: List[str] = None,
1112
+ org_id: str = None,
1113
+ **params
1114
+ ) -> Generator[AvailableNumber, None, None]
1115
+ ```
1116
+
1117
+ #### Get Fax Message Available Phone Numbers
1118
+
1119
+ ```python
1120
+ def fax_message_available_phone_numbers(
1121
+ self,
1122
+ location_id: str,
1123
+ phone_number: List[str] = None,
1124
+ org_id: str = None,
1125
+ **params
1126
+ ) -> Generator[AvailableNumber, None, None]
1127
+ ```
1128
+
1129
+ Lists numbers available to be assigned as the voicemail group's fax message phone number.
1130
+
1131
+ ### Key Data Models
1132
+
1133
+ #### `VoicemailGroup` (List Model)
1134
+
1135
+ | Field | Type | Notes |
1136
+ |-------|------|-------|
1137
+ | `group_id` (alias: `id`) | `str` | Unique identifier |
1138
+ | `name` | `str` | Group name |
1139
+ | `location_name` | `str` | Location name |
1140
+ | `location_id` | `str` | Location ID |
1141
+ | `extension` | `str` | Extension number |
1142
+ | `phone_number` | `str` | Phone number |
1143
+ | `routing_prefix` | `str` | Location routing prefix |
1144
+ | `esn` | `str` | Routing prefix + extension |
1145
+ | `enabled` | `bool` | Whether incoming calls are sent to voicemail |
1146
+ | `toll_free_number` | `bool` | Read-only |
1147
+
1148
+ #### `VoicemailGroupDetail` (Detail/Create/Update Model)
1149
+
1150
+ | Field | Type | Required for Create | Notes |
1151
+ |-------|------|-------------------|-------|
1152
+ | `group_id` (alias: `id`) | `str` | -- | Read-only |
1153
+ | `name` | `str` | **Yes** | Group name |
1154
+ | `phone_number` | `str` | No | Voicemail group phone number |
1155
+ | `extension` | `str` | **Yes** | Extension number |
1156
+ | `first_name` | `str` | **Yes** | **Deprecated** -- use `direct_line_caller_id_name` |
1157
+ | `last_name` | `str` | **Yes** | **Deprecated** -- use `direct_line_caller_id_name` |
1158
+ | `passcode` | `int` | **Yes** | Access passcode |
1159
+ | `enabled` | `bool` | No | For update only |
1160
+ | `language_code` | `str` | No | Default `en_us` in factory |
1161
+ | `greeting` | `Greeting` | No | Greeting type enum |
1162
+ | `greeting_uploaded` | `bool` | -- | Read-only; `True` if custom greeting uploaded |
1163
+ | `greeting_description` | `str` | No | Description for custom greeting |
1164
+ | `message_storage` | `VoicemailMessageStorage` | **Yes** (set by factory) | Storage type config |
1165
+ | `notifications` | `VoicemailNotifications` | **Yes** (set by factory) | Notification settings |
1166
+ | `fax_message` | `VoicemailFax` | **Yes** (set by factory) | Fax receive settings |
1167
+ | `transfer_to_number` | `VoicemailTransferToNumber` | **Yes** (set by factory) | Transfer settings |
1168
+ | `email_copy_of_message` | `VoicemailCopyOfMessage` | **Yes** (set by factory) | Email copy settings |
1169
+ | `voice_message_forwarding_enabled` | `bool` | No | Enable/disable voice message forwarding |
1170
+ | `time_zone` | `str` | No | Undocumented field -- present in API response and wxc_sdk model (`# TODO: undocumented`) but absent from OpenAPI spec `GetLocationVoicemailGroupObject` schema. |
1171
+ | `direct_line_caller_id_name` | `DirectLineCallerIdName` | No | Replaces deprecated first/last name |
1172
+ | `dial_by_name` | `str` | No | Name for dial-by-name directory |
1173
+
1174
+ #### `VoicemailMessageStorage`
1175
+
1176
+ | Field | Type | Notes |
1177
+ |-------|------|-------|
1178
+ | `storage_type` | `StorageType` | `INTERNAL` (Webex-hosted) or `EXTERNAL` (external server) |
1179
+ | `external_email` | `str` | Email address for external storage delivery |
1180
+
1181
+ #### `VoicemailNotifications`
1182
+
1183
+ | Field | Type | Notes |
1184
+ |-------|------|-------|
1185
+ | `enabled` | `bool` | Whether notifications are sent |
1186
+ | `destination` | `str` | Email address or phone number for notifications |
1187
+
1188
+ #### `VoicemailFax`
1189
+
1190
+ | Field | Type | Notes |
1191
+ |-------|------|-------|
1192
+ | `enabled` | `bool` | Whether fax reception is enabled |
1193
+ | `phone_number` | `str` | Dedicated fax phone number |
1194
+
1195
+ #### `VoicemailTransferToNumber`
1196
+
1197
+ | Field | Type | Notes |
1198
+ |-------|------|-------|
1199
+ | `enabled` | `bool` | Whether transfer-to-number is enabled |
1200
+ | `destination` | `str` | Number to transfer to when the caller presses 0 |
1201
+
1202
+ #### `VoicemailCopyOfMessage`
1203
+
1204
+ | Field | Type | Notes |
1205
+ |-------|------|-------|
1206
+ | `enabled` | `bool` | Whether email copies of voicemail are sent |
1207
+ | `email_id` | `str` | Email address to receive message copies |
1208
+
1209
+ The `VoicemailGroupDetail.create()` factory sets these defaults:
1210
+ - `message_storage`: `StorageType.internal`
1211
+ - `notifications`: disabled
1212
+ - `fax_message`: disabled
1213
+ - `transfer_to_number`: disabled
1214
+ - `email_copy_of_message`: disabled
1215
+
1216
+ ### Phone Number / Extension Assignment
1217
+
1218
+ - `extension` is **required** on create.
1219
+ - `phone_number` is optional.
1220
+ - Use `available_phone_numbers()` and `fax_message_available_phone_numbers()` to find unassigned numbers at a location.
1221
+
1222
+ ### Member Management
1223
+
1224
+ Voicemail Groups do not have explicit member lists. They are shared voicemail boxes that are **assigned to** other features (auto attendants, call queues, hunt groups) or users through those features' overflow/no-answer settings.
1225
+
1226
+ ### Raw HTTP
1227
+
1228
+ <!-- Updated by playbook session 2026-03-18 -->
1229
+
1230
+ The Voicemail Groups CLI uses SDK methods (not raw HTTP) for most operations. The SDK base is `telephony/config/voicemailGroups`. The underlying API URLs are:
1231
+
1232
+ ```python
1233
+ from wxc_sdk import WebexSimpleApi
1234
+ api = WebexSimpleApi()
1235
+ BASE = "https://webexapis.com/v1"
1236
+ ```
1237
+
1238
+ **List Voicemail Groups** (org-wide):
1239
+
1240
+ ```python
1241
+ result = api.session.rest_get(f"{BASE}/telephony/config/voicemailGroups",
1242
+ params={"locationId": loc_id, "name": "Support", "max": 1000})
1243
+ items = result["voicemailGroups"] # response key: "voicemailGroups"
1244
+ # Each item: {id, name, locationName, locationId, extension, phoneNumber, routingPrefix, esn, enabled, tollFreeNumber}
1245
+ ```
1246
+
1247
+ **Get Voicemail Group Details:**
1248
+
1249
+ ```python
1250
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/voicemailGroups/{vg_id}")
1251
+ # Returns: name, extension, passcode, languageCode, messageStorage, notifications, faxMessage, etc.
1252
+ ```
1253
+
1254
+ **Create Voicemail Group** -- requires 7+ fields (name, extension, passcode, languageCode, messageStorage, notifications, faxMessage, transferToNumber, emailCopyOfMessage):
1255
+
1256
+ ```python
1257
+ body = {
1258
+ "name": "Support VM",
1259
+ "extension": "8200",
1260
+ "passcode": "740384", # 6+ digits, no sequential/repeating patterns
1261
+ "languageCode": "en_us",
1262
+ "messageStorage": {"storageType": "INTERNAL"},
1263
+ "notifications": {"enabled": False},
1264
+ "faxMessage": {"enabled": False},
1265
+ "transferToNumber": {"enabled": False},
1266
+ "emailCopyOfMessage": {"enabled": False}
1267
+ }
1268
+ result = api.session.rest_post(f"{BASE}/telephony/config/locations/{loc_id}/voicemailGroups", json=body)
1269
+ vg_id = result["id"]
1270
+ ```
1271
+
1272
+ **Gotcha**: The wxc_sdk `VoicemailGroupDetail.for_create()` method has a bug -- missing `by_alias=True`, which sends snake_case keys instead of camelCase. Workaround: `model_dump(mode='json', by_alias=True, exclude_unset=True)`.
1273
+
1274
+ **Update Voicemail Group:**
1275
+
1276
+ ```python
1277
+ api.session.rest_put(f"{BASE}/telephony/config/locations/{loc_id}/voicemailGroups/{vg_id}",
1278
+ json={"name": "New Name", "enabled": True})
1279
+ ```
1280
+
1281
+ **Delete Voicemail Group:**
1282
+
1283
+ ```python
1284
+ api.session.rest_delete(f"{BASE}/telephony/config/locations/{loc_id}/voicemailGroups/{vg_id}")
1285
+ ```
1286
+
1287
+ **Get Available Phone Numbers:**
1288
+
1289
+ ```python
1290
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/voicemailGroups/availablePhoneNumbers",
1291
+ params={"max": 1000})
1292
+ items = result["phoneNumbers"]
1293
+ ```
1294
+
1295
+ **Get Fax Message Available Phone Numbers:**
1296
+
1297
+ ```python
1298
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/voicemailGroups/faxMessage/availablePhoneNumbers",
1299
+ params={"max": 1000})
1300
+ items = result["phoneNumbers"]
1301
+ ```
1302
+
1303
+ ### CLI Examples
1304
+
1305
+ Voicemail Group commands are under the `location-voicemail` command group.
1306
+
1307
+ ```bash
1308
+ # List voicemail groups (org-wide or filtered by location)
1309
+ wxcli location-voicemail list --location-id <loc_id>
1310
+
1311
+ # Get details for a voicemail group
1312
+ wxcli location-voicemail show-voicemail-groups --location-id <loc_id> --voicemail-group-id <vg_id>
1313
+
1314
+ # Create a voicemail group
1315
+ wxcli location-voicemail create --location-id <loc_id> --name "Support VM" --extension "8200" \
1316
+ --json-body '{"passcode": "740384", "languageCode": "en_us", "messageStorage": {"storageType": "INTERNAL"}, "notifications": {"enabled": false}, "faxMessage": {"enabled": false}, "transferToNumber": {"enabled": false}, "emailCopyOfMessage": {"enabled": false}}'
1317
+
1318
+ # Update a voicemail group
1319
+ wxcli location-voicemail update-voicemail-groups --location-id <loc_id> --voicemail-group-id <vg_id> \
1320
+ --name "New Name"
1321
+
1322
+ # Delete a voicemail group
1323
+ wxcli location-voicemail delete --location-id <loc_id> --voicemail-group-id <vg_id>
1324
+
1325
+ # List available phone numbers for voicemail group assignment
1326
+ wxcli location-voicemail list-available-numbers-voicemail-groups --location-id <loc_id>
1327
+
1328
+ # List available phone numbers for fax message
1329
+ wxcli location-voicemail list-available-numbers-fax-message --location-id <loc_id>
1330
+ ```
1331
+
1332
+ ---
1333
+
1334
+ ## Customer Assist (CX Essentials)
1335
+
1336
+ > **Not to be confused with Webex Contact Center (WxCC).** Customer Assist (formerly CX Essentials) is a Webex Calling add-on for basic queue supervision and agent experience. Webex Contact Center is a separate product with its own developer portal at `developer.webex-cx.com`, separate APIs, and separate licensing. This playbook covers Customer Assist only.
1337
+
1338
+ ### Overview
1339
+
1340
+ Webex **Customer Assist** (formerly Customer Experience Essentials / CX Essentials) provides enhanced queue capabilities beyond the Customer Experience Basic suite. It adds features such as:
1341
+
1342
+ - **Screen Pop**: Pop a URL (CRM, ticketing system) when an agent receives a queued call
1343
+ - **Queue Call Recording**: Hosted call recording for quality assurance, compliance, and training
1344
+ - **Wrap-Up Reasons**: Post-call categorization codes that agents select after ending a call
1345
+ - **Customer Assist Agent Licensing**: Ability to query agents with Customer Assist licenses
1346
+
1347
+ These APIs are distinct from Customer Experience Basic and require Customer Assist licensing.
1348
+
1349
+ ### SDK API Class
1350
+
1351
+ ```python
1352
+ @dataclass(init=False, repr=False)
1353
+ class CustomerExperienceEssentialsApi(ApiChild, base='telephony/config')
1354
+ ```
1355
+
1356
+ Sub-APIs:
1357
+
1358
+ ```python
1359
+ api.telephony.cx_essentials.callqueue_recording # QueueCallRecordingSettingsApi
1360
+ api.telephony.cx_essentials.wrapup_reasons # WrapupReasonApi
1361
+ ```
1362
+
1363
+ ### Screen Pop Configuration
1364
+
1365
+ Screen pop lets agents view customer-related info in a pop-up window when receiving a call from a queue.
1366
+
1367
+ #### Get Screen Pop Configuration
1368
+
1369
+ ```python
1370
+ def get_screen_pop_configuration(
1371
+ self,
1372
+ location_id: str = None,
1373
+ queue_id: str = None,
1374
+ org_id: str = None
1375
+ ) -> ScreenPopConfiguration
1376
+ ```
1377
+
1378
+ #### Modify Screen Pop Configuration
1379
+
1380
+ ```python
1381
+ def modify_screen_pop_configuration(
1382
+ self,
1383
+ location_id: str,
1384
+ queue_id: str,
1385
+ settings: ScreenPopConfiguration,
1386
+ org_id: str = None
1387
+ ) -> None
1388
+ ```
1389
+
1390
+ #### `ScreenPopConfiguration` Model
1391
+
1392
+ | Field | Type | Notes |
1393
+ |-------|------|-------|
1394
+ | `enabled` | `bool` | Enable/disable screen pop |
1395
+ | `screen_pop_url` | `str` | URL to pop (CRM, ticketing, etc.) |
1396
+ | `desktop_label` | `str` | Label for the screen pop config |
1397
+ | `query_params` | `dict[str, Any]` | Query parameters appended to the screen pop URL |
1398
+
1399
+ ### Available Agents
1400
+
1401
+ #### List Customer Assist Available Agents
1402
+
1403
+ ```python
1404
+ def available_agents(
1405
+ self,
1406
+ location_id: str,
1407
+ has_cx_essentials: bool = None,
1408
+ org_id: str = None
1409
+ ) -> Generator[AvailableAgent, None, None]
1410
+ ```
1411
+
1412
+ - `has_cx_essentials=True`: Returns only agents with Customer Assist (formerly CX Essentials) license.
1413
+ - `has_cx_essentials=False`: Returns only agents with CX Basic license.
1414
+ - Omit for all agents.
1415
+
1416
+ ### Queue Call Recording
1417
+
1418
+ Hosted call recording for queues. Records calls placed and received on the platform for replay and archival.
1419
+
1420
+ #### Read Queue Call Recording Settings
1421
+
1422
+ ```python
1423
+ def read(
1424
+ self,
1425
+ location_id: str,
1426
+ queue_id: str,
1427
+ org_id: str = None
1428
+ ) -> CallRecordingSetting
1429
+ ```
1430
+
1431
+ **Scope**: `spark-admin:people_read`
1432
+
1433
+ #### Configure Queue Call Recording Settings
1434
+
1435
+ ```python
1436
+ def configure(
1437
+ self,
1438
+ location_id: str,
1439
+ queue_id: str,
1440
+ recording: CallRecordingSetting,
1441
+ org_id: str = None
1442
+ ) -> None
1443
+ ```
1444
+
1445
+ **Scope**: `spark-admin:people_write`
1446
+
1447
+ **Note**: A person with a Webex Calling Standard license is eligible for call recording only when the recording vendor is Webex.
1448
+
1449
+ #### Raw HTTP
1450
+
1451
+ ```
1452
+ GET /v1/telephony/config/locations/{locationId}/queues/{queueId}/cxEssentials/callRecordings
1453
+ PUT /v1/telephony/config/locations/{locationId}/queues/{queueId}/cxEssentials/callRecordings
1454
+ ```
1455
+
1456
+ GET response / PUT body fields: `enabled` (bool), `record` (str: Always/Never/OnDemand), `notification` (object: `{enabled, type}`), `repeat` (object: `{enabled, interval}`), `startStopAnnouncement` (object: `{internalCallsEnabled, pstnCallsEnabled}`), `serviceProvider` (str), `externalGroup` (str), `externalIdentifier` (str).
1457
+
1458
+ Scope: `spark-admin:people_read` (GET), `spark-admin:people_write` (PUT).
1459
+
1460
+ ### Wrap-Up Reasons
1461
+
1462
+ Wrap-up reasons let agents categorize the outcome of a call after it ends. Admins configure reasons and assign them to queues. A configurable timer (default 60 seconds) dictates how long agents have to select a reason post-call.
1463
+
1464
+ #### List Wrap-Up Reasons (Org-Level)
1465
+
1466
+ ```python
1467
+ def list(self) -> List[WrapUpReason]
1468
+ ```
1469
+
1470
+ Returns all wrap-up reasons configured for the organization. No parameters needed.
1471
+
1472
+ #### Get Wrap-Up Reason Details
1473
+
1474
+ ```python
1475
+ def details(
1476
+ self,
1477
+ wrapup_reason_id: str
1478
+ ) -> WrapUpReasonDetails
1479
+ ```
1480
+
1481
+ Returns the reason details including assigned queues.
1482
+
1483
+ #### Create Wrap-Up Reason
1484
+
1485
+ ```python
1486
+ def create(
1487
+ self,
1488
+ name: str,
1489
+ description: str = None,
1490
+ queues: List[str] = None,
1491
+ assign_all_queues_enabled: bool = None
1492
+ ) -> str # returns wrap-up reason ID
1493
+ ```
1494
+
1495
+ - `queues`: List of queue IDs to assign.
1496
+ - `assign_all_queues_enabled`: Assign to all queues at once.
1497
+
1498
+ #### Update Wrap-Up Reason
1499
+
1500
+ ```python
1501
+ def update(
1502
+ self,
1503
+ wrapup_reason_id: str,
1504
+ name: str = None,
1505
+ description: str = None,
1506
+ queues_to_assign: List[str] = None,
1507
+ queues_to_unassign: List[str] = None,
1508
+ assign_all_queues_enabled: bool = None,
1509
+ unassign_all_queues_enabled: bool = None
1510
+ ) -> None
1511
+ ```
1512
+
1513
+ Queue assignment is **incremental** -- you can assign and unassign specific queues without replacing the full list.
1514
+
1515
+ #### Delete Wrap-Up Reason
1516
+
1517
+ ```python
1518
+ def delete(
1519
+ self,
1520
+ wrapup_reason_id: str
1521
+ ) -> None
1522
+ ```
1523
+
1524
+ #### Validate Wrap-Up Reason Name
1525
+
1526
+ ```python
1527
+ def validate(
1528
+ self,
1529
+ name: str
1530
+ ) -> None
1531
+ ```
1532
+
1533
+ Check if a wrap-up reason name is valid (not already taken). Raises an error on conflict.
1534
+
1535
+ #### Read Queue Wrap-Up Settings
1536
+
1537
+ ```python
1538
+ def read_queue_settings(
1539
+ self,
1540
+ location_id: str,
1541
+ queue_id: str
1542
+ ) -> QueueWrapupReasonSettings
1543
+ ```
1544
+
1545
+ Returns wrap-up configuration for a specific queue: timer settings and assigned reasons.
1546
+
1547
+ #### Update Queue Wrap-Up Settings
1548
+
1549
+ ```python
1550
+ def update_queue_settings(
1551
+ self,
1552
+ location_id: str,
1553
+ queue_id: str,
1554
+ wrapup_reasons: list[str] = None,
1555
+ default_wrapup_reason_id: str = None,
1556
+ wrapup_timer_enabled: bool = None,
1557
+ wrapup_timer: int = None
1558
+ ) -> None
1559
+ ```
1560
+
1561
+ - `default_wrapup_reason_id`: Set as `''` (empty string) to clear the default.
1562
+ - `wrapup_timer`: Timer value in seconds (default is 60).
1563
+
1564
+ #### Get Available Queues for Wrap-Up Reason
1565
+
1566
+ ```python
1567
+ def available_queues(
1568
+ self,
1569
+ wrapup_reason_id: str
1570
+ ) -> List[AvailableQueue]
1571
+ ```
1572
+
1573
+ Returns queues that are not yet assigned to this wrap-up reason and can be added.
1574
+
1575
+ ### Customer Assist Data Models
1576
+
1577
+ #### `WrapUpReason` (List Model)
1578
+
1579
+ | Field | Type | Notes |
1580
+ |-------|------|-------|
1581
+ | `id` | `str` | Unique identifier |
1582
+ | `name` | `str` | Reason name |
1583
+ | `description` | `str` | Reason description |
1584
+ | `number_of_queues_assigned` | `int` | Count of assigned queues |
1585
+
1586
+ #### `WrapUpReasonDetails` (Detail Model)
1587
+
1588
+ | Field | Type | Notes |
1589
+ |-------|------|-------|
1590
+ | `name` | `str` | Reason name |
1591
+ | `description` | `str` | Reason description |
1592
+ | `default_wrapup_queues_count` | `int` | Number of queues where this is the default |
1593
+ | `queues` | `list[WrapupReasonQueue]` | Assigned queues with full details |
1594
+
1595
+ #### `WrapupReasonQueue`
1596
+
1597
+ | Field | Type | Notes |
1598
+ |-------|------|-------|
1599
+ | `id` | `str` | Queue ID |
1600
+ | `name` | `str` | Queue name |
1601
+ | `location_name` | `str` | Location name |
1602
+ | `location_id` | `str` | Location ID |
1603
+ | `phone_number` | `str` | Queue phone number |
1604
+ | `extension` | `int` | Queue extension |
1605
+ | `default_wrapup_enabled` | `bool` | Whether this reason is the default for the queue |
1606
+
1607
+ #### `QueueWrapupReasonSettings`
1608
+
1609
+ | Field | Type | Notes |
1610
+ |-------|------|-------|
1611
+ | `wrapup_timer_enabled` | `bool` | Whether the wrap-up timer is active |
1612
+ | `wrapup_timer` | `int` | Timer value in seconds |
1613
+ | `wrapup_reasons` | `list[QueueSettingsReason]` | Reasons assigned to this queue |
1614
+
1615
+ #### `QueueSettingsReason`
1616
+
1617
+ | Field | Type | Notes |
1618
+ |-------|------|-------|
1619
+ | `id` | `str` | Wrap-up reason ID |
1620
+ | `name` | `str` | Reason name |
1621
+ | `description` | `str` | Reason description |
1622
+ | `default_enabled` | `bool` | Whether this reason is the default for the queue |
1623
+
1624
+ #### `AvailableQueue`
1625
+
1626
+ | Field | Type | Notes |
1627
+ |-------|------|-------|
1628
+ | `id` | `str` | Queue ID |
1629
+ | `name` | `str` | Queue name |
1630
+ | `location_name` | `str` | Location name |
1631
+ | `location_id` | `str` | Location ID |
1632
+ | `phone_number` | `str` | Queue phone number |
1633
+ | `extension` | `int` | Queue extension |
1634
+
1635
+ ### Raw HTTP (Customer Assist)
1636
+
1637
+ <!-- Updated by playbook session 2026-03-18 -->
1638
+
1639
+ Customer Assist (formerly CX Essentials) APIs operate on call queues. The base URL pattern is `telephony/config/cxEssentials/...` or queue-scoped paths.
1640
+
1641
+ ```python
1642
+ from wxc_sdk import WebexSimpleApi
1643
+ api = WebexSimpleApi()
1644
+ BASE = "https://webexapis.com/v1"
1645
+ ```
1646
+
1647
+ **Note**: Customer Assist operations require Customer Assist licensing. Screen pop, queue recording, and wrap-up reasons are all per-queue settings -- call queues must exist first.
1648
+
1649
+ ### CLI Examples
1650
+
1651
+ ```bash
1652
+ # List all wrap-up reasons (org-level)
1653
+ wxcli cx-essentials list
1654
+
1655
+ # Get details for a wrap-up reason
1656
+ wxcli cx-essentials show REASON_ID -o json
1657
+
1658
+ # Create a wrap-up reason
1659
+ wxcli cx-essentials create --name "Customer Inquiry"
1660
+
1661
+ # Update a wrap-up reason
1662
+ wxcli cx-essentials update REASON_ID --name "Updated Reason"
1663
+
1664
+ # Delete a wrap-up reason
1665
+ wxcli cx-essentials delete REASON_ID --force
1666
+
1667
+ # Validate a wrap-up reason name
1668
+ wxcli cx-essentials validate-wrap-up --name "New Reason Name"
1669
+
1670
+ # List available queues for a wrap-up reason
1671
+ wxcli cx-essentials list-available-queues REASON_ID
1672
+
1673
+ # Get wrap-up reason settings for a queue
1674
+ wxcli cx-essentials list-settings LOCATION_ID QUEUE_ID -o json
1675
+
1676
+ # Update wrap-up reason settings for a queue
1677
+ wxcli cx-essentials update-settings LOCATION_ID QUEUE_ID \
1678
+ --json-body '{"wrapupTimerEnabled": true, "wrapupTimer": 60}'
1679
+
1680
+ # Get screen pop configuration for a queue
1681
+ wxcli cx-essentials show-screen-pop LOCATION_ID QUEUE_ID -o json
1682
+
1683
+ # Update screen pop configuration for a queue
1684
+ wxcli cx-essentials update-screen-pop LOCATION_ID QUEUE_ID \
1685
+ --json-body '{"enabled": true, "screenPopUrl": "https://crm.example.com/pop"}'
1686
+
1687
+ # List available agents (optionally filter by Customer Assist license)
1688
+ wxcli cx-essentials list-available-agents LOCATION_ID
1689
+
1690
+ # Get queue call recording settings
1691
+ wxcli cx-essentials show-queue-recording LOCATION_ID QUEUE_ID -o json
1692
+
1693
+ # Update queue call recording settings
1694
+ wxcli cx-essentials update-queue-recording LOCATION_ID QUEUE_ID \
1695
+ --enabled --record Always
1696
+
1697
+ # --- Queue Discovery & Creation ---
1698
+
1699
+ # List Customer Assist queues (hidden from default call-queue list!)
1700
+ wxcli call-queue list --has-cx-essentials true -o json
1701
+
1702
+ # Create a Customer Assist queue (requires callPolicies via --json-body)
1703
+ wxcli call-queue create LOCATION_ID --name "Queue Name" --has-cx-essentials true \
1704
+ --json-body '{"name":"Queue Name","extension":"XXXX","callPolicies":{"policy":"SIMULTANEOUS"}}'
1705
+
1706
+ # --- Supervisor Cleanup ---
1707
+
1708
+ # To remove a supervisor, remove all agents via update (not delete endpoint)
1709
+ wxcli call-queue update-supervisors SUPERVISOR_ID --has-cx-essentials true \
1710
+ --json-body '{"agents":[{"id":"AGENT_ID","action":"DELETE"}]}'
1711
+ # When last agent is removed, supervisor is auto-deleted
1712
+ ```
1713
+
1714
+ > **GOTCHA:** Customer Assist queues do not appear in the default `wxcli call-queue list` output. You must pass `--has-cx-essentials true` to see them. Using CX Essentials endpoints on a regular queue returns error 28018 ("CX Essentials is not enabled for this Call center"). The CLI detects this error and prints a tip.
1715
+
1716
+ > **GOTCHA:** Creating a Customer Assist queue requires `callPolicies` in the request body via `--json-body`. Omitting `callPolicies` results in a 400 error. Minimum: `{"callPolicies":{"policy":"SIMULTANEOUS"}}`.
1717
+
1718
+ > **GOTCHA:** `delete-supervisors-config-1` returns 204 but the supervisor persists. Use `update-supervisors` with `action: DELETE` on each agent instead — removing the last agent auto-removes the supervisor.
1719
+
1720
+ ---
1721
+
1722
+ ## Operating Modes
1723
+
1724
+ <!-- Updated by playbook session 2026-03-18 -->
1725
+
1726
+ > **Not supported for Webex for Government (FedRAMP).** See [authentication.md → FedRAMP](authentication.md#webex-for-government-fedramp) for all FedRAMP restrictions.
1727
+
1728
+ ### Overview
1729
+
1730
+ Operating Modes define time-of-day schedules (business hours, after hours, holidays) that control call routing behavior for features like auto attendants and call queues. They can be defined at the organization level and assigned to locations.
1731
+
1732
+ Types: `SAME_HOURS_DAILY`, `DIFFERENT_HOURS_DAILY`, `HOLIDAY`.
1733
+
1734
+ ### Raw HTTP
1735
+
1736
+ ```python
1737
+ from wxc_sdk import WebexSimpleApi
1738
+ api = WebexSimpleApi()
1739
+ BASE = "https://webexapis.com/v1"
1740
+ ```
1741
+
1742
+ **List Operating Modes** (org-wide):
1743
+
1744
+ ```python
1745
+ result = api.session.rest_get(f"{BASE}/telephony/config/operatingModes",
1746
+ params={"name": "Business", "limitToLocationId": loc_id,
1747
+ "limitToOrgLevelEnabled": "true", "order": "ASC", "max": 1000})
1748
+ items = result["operatingModes"] # response key: "operatingModes"
1749
+ # Each item: {id, name}
1750
+ ```
1751
+
1752
+ **Get Operating Mode Details:**
1753
+
1754
+ ```python
1755
+ result = api.session.rest_get(f"{BASE}/telephony/config/operatingModes/{mode_id}")
1756
+ # Returns full schedule configuration
1757
+ ```
1758
+
1759
+ **Create Operating Mode** -- required: `name`, `type`, `level`:
1760
+
1761
+ ```python
1762
+ body = {
1763
+ "name": "Business Hours",
1764
+ "type": "SAME_HOURS_DAILY", # SAME_HOURS_DAILY | DIFFERENT_HOURS_DAILY | HOLIDAY
1765
+ "level": "ORGANIZATION", # required: must be ORGANIZATION
1766
+ "locationId": loc_id # optional: scope to a location
1767
+ }
1768
+ result = api.session.rest_post(f"{BASE}/telephony/config/operatingModes/", json=body)
1769
+ mode_id = result["id"]
1770
+ ```
1771
+
1772
+ **Update Operating Mode:**
1773
+
1774
+ ```python
1775
+ api.session.rest_put(f"{BASE}/telephony/config/operatingModes/{mode_id}",
1776
+ json={"name": "Updated Hours"})
1777
+ ```
1778
+
1779
+ **Delete Operating Mode:**
1780
+
1781
+ ```python
1782
+ api.session.rest_delete(f"{BASE}/telephony/config/operatingModes/{mode_id}")
1783
+ ```
1784
+
1785
+ **Create Holiday** (HOLIDAY type only) -- required: `name`, `startDate`, `endDate`, `allDayEnabled`:
1786
+
1787
+ ```python
1788
+ body = {
1789
+ "name": "Christmas 2026",
1790
+ "allDayEnabled": True,
1791
+ "startDate": "2026-12-25", # note: startDate/endDate, not date
1792
+ "endDate": "2026-12-25"
1793
+ }
1794
+ result = api.session.rest_post(f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays", json=body)
1795
+ holiday_id = result["id"]
1796
+ ```
1797
+
1798
+ **Get / Update / Delete Holiday:**
1799
+
1800
+ ```python
1801
+ # Get
1802
+ result = api.session.rest_get(f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays/{holiday_id}")
1803
+
1804
+ # Update
1805
+ api.session.rest_put(f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays/{holiday_id}",
1806
+ json={"name": "New Year", "startDate": "2027-01-01", "endDate": "2027-01-01", "allDayEnabled": True})
1807
+
1808
+ # Delete
1809
+ api.session.rest_delete(f"{BASE}/telephony/config/operatingModes/{mode_id}/holidays/{holiday_id}")
1810
+ ```
1811
+
1812
+ **List Available Operating Modes for Location:**
1813
+
1814
+ ```python
1815
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/operatingModes/availableOperatingModes",
1816
+ params={"max": 1000})
1817
+ items = result["availableOperatingModes"]
1818
+ ```
1819
+
1820
+ **Get Operating Mode Call Forward Available Numbers:**
1821
+
1822
+ ```python
1823
+ result = api.session.rest_get(
1824
+ f"{BASE}/telephony/config/locations/{loc_id}/operatingModes/callForwarding/availableNumbers",
1825
+ params={"max": 1000})
1826
+ items = result["availableNumbers"]
1827
+ ```
1828
+
1829
+ ### CLI Examples
1830
+
1831
+ ```bash
1832
+ # List operating modes (org-wide)
1833
+ wxcli operating-modes list
1834
+
1835
+ # List operating modes filtered by location
1836
+ wxcli operating-modes list --limit-to-location-id <loc_id>
1837
+
1838
+ # Get details for an operating mode
1839
+ wxcli operating-modes show --operating-mode-id <mode_id>
1840
+
1841
+ # Create an operating mode
1842
+ wxcli operating-modes create --name "Business Hours" --type "SAME_HOURS_DAILY" --level "ORGANIZATION"
1843
+
1844
+ # Update an operating mode
1845
+ wxcli operating-modes update --operating-mode-id <mode_id> --name "Updated Hours"
1846
+
1847
+ # Delete an operating mode
1848
+ wxcli operating-modes delete --operating-mode-id <mode_id>
1849
+
1850
+ # Create a holiday on a HOLIDAY-type operating mode
1851
+ wxcli operating-modes create-holidays --operating-mode-id <mode_id> \
1852
+ --name "Christmas 2026" --all-day-enabled --start-date "2026-12-25" --end-date "2026-12-25"
1853
+
1854
+ # Get holiday details
1855
+ wxcli operating-modes show-holidays --operating-mode-id <mode_id> --holiday-id <holiday_id>
1856
+
1857
+ # Update a holiday
1858
+ wxcli operating-modes update-holidays --operating-mode-id <mode_id> --holiday-id <holiday_id> \
1859
+ --name "New Year" --start-date "2027-01-01" --end-date "2027-01-01"
1860
+
1861
+ # Delete a holiday
1862
+ wxcli operating-modes delete-holidays --operating-mode-id <mode_id> --holiday-id <holiday_id>
1863
+
1864
+ # List available operating modes for a location
1865
+ wxcli operating-modes list-available-operating-modes --location-id <loc_id>
1866
+
1867
+ # Get available call forward numbers for operating modes
1868
+ wxcli operating-modes list-available-numbers --location-id <loc_id>
1869
+ ```
1870
+
1871
+ ### Gotchas
1872
+
1873
+ - OM create requires `level=ORGANIZATION` -- other values fail.
1874
+ - `type` must be one of: `SAME_HOURS_DAILY`, `DIFFERENT_HOURS_DAILY`, `HOLIDAY`.
1875
+ - `sameHoursDaily` needs actual `DaySchedule` hours configured to take effect.
1876
+ - Holiday create uses `startDate`/`endDate` fields (not `date`), plus `allDayEnabled`.
1877
+ - Holidays can only be added to `HOLIDAY` type operating modes.
1878
+
1879
+ ---
1880
+
1881
+ ## Call Recording (Org-Level)
1882
+
1883
+ <!-- Updated by playbook session 2026-03-18 -->
1884
+
1885
+ ### Overview
1886
+
1887
+ Organization-level call recording settings control global recording behavior. Per-queue recording is managed through Customer Assist (see above). Per-person recording is managed through person call settings.
1888
+
1889
+ ### Raw HTTP
1890
+
1891
+ ```python
1892
+ from wxc_sdk import WebexSimpleApi
1893
+ api = WebexSimpleApi()
1894
+ BASE = "https://webexapis.com/v1"
1895
+ ```
1896
+
1897
+ **Get Org Call Recording Settings:**
1898
+
1899
+ ```python
1900
+ result = api.session.rest_get(f"{BASE}/telephony/config/callRecording")
1901
+ # Returns: enabled, record, vendor info, etc.
1902
+ ```
1903
+
1904
+ **Update Org Call Recording Settings:**
1905
+
1906
+ ```python
1907
+ api.session.rest_put(f"{BASE}/telephony/config/callRecording",
1908
+ json={"enabled": True})
1909
+ ```
1910
+
1911
+ **Get Call Recording Terms of Service:**
1912
+
1913
+ ```python
1914
+ result = api.session.rest_get(f"{BASE}/telephony/config/callRecording/vendors/{vendor_id}/termsOfService")
1915
+ ```
1916
+
1917
+ ### CLI Examples
1918
+
1919
+ ```bash
1920
+ # Get org-level call recording settings
1921
+ wxcli call-recording show
1922
+
1923
+ # Update org-level call recording settings
1924
+ wxcli call-recording update --json-body '{"enabled": true}'
1925
+
1926
+ # Get call recording terms of service
1927
+ wxcli call-recording show-terms-of-service --vendor-id <vendor_id>
1928
+
1929
+ # Update call recording terms of service
1930
+ wxcli call-recording update-terms-of-service --vendor-id <vendor_id> --json-body '{"termsOfServiceEnabled": true}'
1931
+
1932
+ # Get org-level compliance announcement settings
1933
+ wxcli call-recording show-compliance-announcement-call-recording
1934
+
1935
+ # Update org-level compliance announcement settings
1936
+ wxcli call-recording update-compliance-announcement-call-recording --json-body '{"enabled": true}'
1937
+
1938
+ # Get location-level compliance announcement settings
1939
+ wxcli call-recording show-compliance-announcement-call-recording-1 --location-id <loc_id>
1940
+
1941
+ # Update location-level compliance announcement settings
1942
+ wxcli call-recording update-compliance-announcement-call-recording-1 --location-id <loc_id> --json-body '{"enabled": true}'
1943
+
1944
+ # List call recording regions
1945
+ wxcli call-recording list
1946
+
1947
+ # List call recording vendors for a location
1948
+ wxcli call-recording list-vendors --location-id <loc_id>
1949
+
1950
+ # Get org-level call recording vendors
1951
+ wxcli call-recording show-vendors
1952
+
1953
+ # Set call recording vendor for a location
1954
+ wxcli call-recording update-vendor-call-recording --location-id <loc_id> --json-body '{"vendorId": "<vendor_id>"}'
1955
+
1956
+ # List call recording jobs
1957
+ wxcli call-recording list-call-recording
1958
+
1959
+ # Get job status of a call recording job
1960
+ wxcli call-recording show-call-recording --job-id <job_id>
1961
+ ```
1962
+
1963
+ ---
1964
+
1965
+ ## Announcements & Playlists
1966
+
1967
+ <!-- Updated by playbook session 2026-03-18 -->
1968
+
1969
+ ### Overview
1970
+
1971
+ The Announcement Repository stores binary audio greeting files (WAV, WMA) used by auto attendants, call queues, hunt groups, and other features. Announcements can be managed at the organization level or scoped to a location. Playlists group multiple announcements for sequential playback.
1972
+
1973
+ ### Raw HTTP
1974
+
1975
+ ```python
1976
+ from wxc_sdk import WebexSimpleApi
1977
+ api = WebexSimpleApi()
1978
+ BASE = "https://webexapis.com/v1"
1979
+ ```
1980
+
1981
+ #### Announcements
1982
+
1983
+ **List Announcements** (org-level, filterable by location):
1984
+
1985
+ ```python
1986
+ result = api.session.rest_get(f"{BASE}/telephony/config/announcements",
1987
+ params={"locationId": loc_id, "fileName": "greeting", "fileType": "WAV",
1988
+ "name": "Welcome", "order": "fileName", "max": 1000})
1989
+ items = result["announcements"] # response key: "announcements"
1990
+ # Each item: {id, name, fileName, fileSize, mediaFileType, level, locationId, locationName}
1991
+ ```
1992
+
1993
+ **Get Announcement Details:**
1994
+
1995
+ ```python
1996
+ result = api.session.rest_get(f"{BASE}/telephony/config/announcements/{ann_id}")
1997
+ # Org-level announcement
1998
+ ```
1999
+
2000
+ **Get Location-Level Announcement Details:**
2001
+
2002
+ ```python
2003
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/announcements/{ann_id}")
2004
+ ```
2005
+
2006
+ **Update Announcement** (org-level):
2007
+
2008
+ ```python
2009
+ api.session.rest_put(f"{BASE}/telephony/config/announcements/{ann_id}", json={...})
2010
+ ```
2011
+
2012
+ **Update Announcement** (location-level):
2013
+
2014
+ ```python
2015
+ api.session.rest_put(f"{BASE}/telephony/config/locations/{loc_id}/announcements/{ann_id}", json={...})
2016
+ ```
2017
+
2018
+ **Delete Announcement** (org-level):
2019
+
2020
+ ```python
2021
+ api.session.rest_delete(f"{BASE}/telephony/config/announcements/{ann_id}")
2022
+ ```
2023
+
2024
+ **Delete Announcement** (location-level):
2025
+
2026
+ ```python
2027
+ api.session.rest_delete(f"{BASE}/telephony/config/locations/{loc_id}/announcements/{ann_id}")
2028
+ ```
2029
+
2030
+ **Get Repository Usage** (org-level):
2031
+
2032
+ ```python
2033
+ result = api.session.rest_get(f"{BASE}/telephony/config/announcements/usage")
2034
+ items = result["usage"]
2035
+ ```
2036
+
2037
+ **Get Repository Usage** (location-level):
2038
+
2039
+ ```python
2040
+ result = api.session.rest_get(f"{BASE}/telephony/config/locations/{loc_id}/announcements/usage")
2041
+ items = result["usage"]
2042
+ ```
2043
+
2044
+ **Note**: Announcement upload (create) requires multipart/form-data with the binary file. The raw HTTP `rest_post` method may not support file uploads directly -- use the SDK's upload methods or construct multipart requests manually.
2045
+
2046
+ #### Playlists
2047
+
2048
+ **List Playlists:**
2049
+
2050
+ ```python
2051
+ result = api.session.rest_get(f"{BASE}/telephony/config/announcements/playlists",
2052
+ params={"max": 1000})
2053
+ items = result["playlists"] # response key: "playlists"
2054
+ ```
2055
+
2056
+ **Get Playlist Details:**
2057
+
2058
+ ```python
2059
+ result = api.session.rest_get(f"{BASE}/telephony/config/announcements/playlists/{playlist_id}")
2060
+ ```
2061
+
2062
+ **Create Playlist:**
2063
+
2064
+ ```python
2065
+ result = api.session.rest_post(f"{BASE}/telephony/config/announcements/playlists",
2066
+ json={"name": "Hold Music"})
2067
+ playlist_id = result["id"]
2068
+ ```
2069
+
2070
+ **Update Playlist:**
2071
+
2072
+ ```python
2073
+ api.session.rest_put(f"{BASE}/telephony/config/announcements/playlists/{playlist_id}",
2074
+ json={"name": "Updated Playlist"})
2075
+ ```
2076
+
2077
+ **Delete Playlist:**
2078
+
2079
+ ```python
2080
+ api.session.rest_delete(f"{BASE}/telephony/config/announcements/playlists/{playlist_id}")
2081
+ ```
2082
+
2083
+ **List Playlist Locations:**
2084
+
2085
+ ```python
2086
+ result = api.session.rest_get(f"{BASE}/telephony/config/announcements/playlists/{playlist_id}/locations",
2087
+ params={"max": 1000})
2088
+ items = result["locations"]
2089
+ ```
2090
+
2091
+ **Update Playlist Locations:**
2092
+
2093
+ ```python
2094
+ api.session.rest_put(f"{BASE}/telephony/config/announcements/playlists/{playlist_id}/locations",
2095
+ json={"locationIds": [loc_id_1, loc_id_2]})
2096
+ ```
2097
+
2098
+ ### CLI Examples
2099
+
2100
+ #### Announcements
2101
+
2102
+ ```bash
2103
+ # List announcements (org-level, optionally filtered by location)
2104
+ wxcli announcements list --location-id <loc_id>
2105
+
2106
+ # Get org-level announcement details
2107
+ wxcli announcements show-announcements-config --announcement-id <ann_id>
2108
+
2109
+ # Get location-level announcement details
2110
+ wxcli announcements show-announcements-locations --location-id <loc_id> --announcement-id <ann_id>
2111
+
2112
+ # Update an org-level announcement
2113
+ wxcli announcements update --announcement-id <ann_id> --json-body '{"name": "Updated Greeting"}'
2114
+
2115
+ # Update a location-level announcement
2116
+ wxcli announcements update-announcements --location-id <loc_id> --announcement-id <ann_id> --json-body '{"name": "Updated"}'
2117
+
2118
+ # Delete an org-level announcement
2119
+ wxcli announcements delete --announcement-id <ann_id>
2120
+
2121
+ # Delete a location-level announcement
2122
+ wxcli announcements delete-announcements --location-id <loc_id> --announcement-id <ann_id>
2123
+
2124
+ # Get org-level repository usage
2125
+ wxcli announcements show
2126
+
2127
+ # Get location-level repository usage
2128
+ wxcli announcements show-usage --location-id <loc_id>
2129
+ ```
2130
+
2131
+ #### Playlists
2132
+
2133
+ ```bash
2134
+ # List announcement playlists
2135
+ wxcli announcement-playlists list
2136
+
2137
+ # Get playlist details
2138
+ wxcli announcement-playlists show --playlist-id <playlist_id>
2139
+
2140
+ # Create a playlist
2141
+ wxcli announcement-playlists create --name "Hold Music"
2142
+
2143
+ # Update a playlist
2144
+ wxcli announcement-playlists update --playlist-id <playlist_id> --name "Updated Playlist"
2145
+
2146
+ # Delete a playlist
2147
+ wxcli announcement-playlists delete --playlist-id <playlist_id>
2148
+
2149
+ # List playlist locations
2150
+ wxcli announcement-playlists list-playlists --playlist-id <playlist_id>
2151
+
2152
+ # Update playlist locations
2153
+ wxcli announcement-playlists update-playlists --playlist-id <playlist_id> \
2154
+ --json-body '{"locationIds": ["<loc_id_1>", "<loc_id_2>"]}'
2155
+ ```
2156
+
2157
+ ---
2158
+
2159
+ ## Single Number Reach
2160
+
2161
+ <!-- Updated by playbook session 2026-03-18 -->
2162
+
2163
+ ### Overview
2164
+
2165
+ Single Number Reach (Office Anywhere) allows a person to receive calls on alternate phone numbers (mobile, home) when their primary Webex line rings. Calls are forwarded simultaneously or sequentially to the configured numbers.
2166
+
2167
+ ### Raw HTTP
2168
+
2169
+ ```python
2170
+ from wxc_sdk import WebexSimpleApi
2171
+ api = WebexSimpleApi()
2172
+ BASE = "https://webexapis.com/v1"
2173
+ ```
2174
+
2175
+ **Get Single Number Reach Settings for a Person:**
2176
+
2177
+ ```python
2178
+ result = api.session.rest_get(f"{BASE}/telephony/config/people/{person_id}/singleNumberReach")
2179
+ # Returns: alertAllNumbersForClickToDialCallsEnabled, numbers[]
2180
+ ```
2181
+
2182
+ **Update Single Number Reach Settings for a Person:**
2183
+
2184
+ ```python
2185
+ api.session.rest_put(f"{BASE}/telephony/config/people/{person_id}/singleNumberReach",
2186
+ json={"alertAllNumbersForClickToDialCallsEnabled": True})
2187
+ ```
2188
+
2189
+ **Create a Single Number Reach Number:**
2190
+
2191
+ ```python
2192
+ body = {
2193
+ "phoneNumber": "+14155559999",
2194
+ "enabled": True,
2195
+ "name": "Mobile",
2196
+ "doNotForwardCallsEnabled": False,
2197
+ "answerConfirmationEnabled": True
2198
+ }
2199
+ result = api.session.rest_post(
2200
+ f"{BASE}/telephony/config/people/{person_id}/singleNumberReach/numbers", json=body)
2201
+ ```
2202
+
2203
+ **Update a Single Number Reach Number:**
2204
+
2205
+ ```python
2206
+ api.session.rest_put(
2207
+ f"{BASE}/telephony/config/people/{person_id}/singleNumberReach/numbers/{number_id}",
2208
+ json={"enabled": False, "name": "Home"})
2209
+ ```
2210
+
2211
+ **Delete a Single Number Reach Number:**
2212
+
2213
+ ```python
2214
+ api.session.rest_delete(
2215
+ f"{BASE}/telephony/config/people/{person_id}/singleNumberReach/numbers/{number_id}")
2216
+ ```
2217
+
2218
+ **Get Available Phone Numbers for Single Number Reach:**
2219
+
2220
+ ```python
2221
+ result = api.session.rest_get(
2222
+ f"{BASE}/telephony/config/locations/{loc_id}/singleNumberReach/availableNumbers",
2223
+ params={"max": 1000})
2224
+ items = result["availableNumbers"]
2225
+ ```
2226
+
2227
+ ### CLI Examples
2228
+
2229
+ ```bash
2230
+ # Get single number reach settings for a person
2231
+ wxcli single-number-reach list-single-number-reach --person-id <person_id>
2232
+
2233
+ # Create a single number reach number for a person
2234
+ wxcli single-number-reach create --person-id <person_id> \
2235
+ --json-body '{"phoneNumber": "+14155559999", "enabled": true, "name": "Mobile", "answerConfirmationEnabled": true}'
2236
+
2237
+ # Update single number reach settings for a person
2238
+ wxcli single-number-reach update --person-id <person_id> \
2239
+ --json-body '{"alertAllNumbersForClickToDialCallsEnabled": true}'
2240
+
2241
+ # Update a specific single number reach number
2242
+ wxcli single-number-reach update-numbers --person-id <person_id> --number-id <number_id> \
2243
+ --json-body '{"enabled": false, "name": "Home"}'
2244
+
2245
+ # Delete a single number reach number
2246
+ wxcli single-number-reach delete --person-id <person_id> --number-id <number_id>
2247
+
2248
+ # List available phone numbers for single number reach
2249
+ wxcli single-number-reach list --location-id <loc_id>
2250
+ ```
2251
+
2252
+ ---
2253
+
2254
+ ## Virtual Extensions
2255
+
2256
+ <!-- Updated by playbook session 2026-03-18 -->
2257
+
2258
+ ### Overview
2259
+
2260
+ Virtual Extensions are directory entries with extensions (and optionally phone numbers) that are not tied to a person, workspace, or device. They appear in the dial-by-name directory and can be used for call routing. Virtual Extension Ranges allow bulk creation with a shared prefix.
2261
+
2262
+ ### Raw HTTP
2263
+
2264
+ ```python
2265
+ from wxc_sdk import WebexSimpleApi
2266
+ api = WebexSimpleApi()
2267
+ BASE = "https://webexapis.com/v1"
2268
+ ```
2269
+
2270
+ #### Virtual Extensions
2271
+
2272
+ **List Virtual Extensions** (org-wide, filterable):
2273
+
2274
+ ```python
2275
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensions",
2276
+ params={"locationId": loc_id, "name": "Lobby", "extension": "9000",
2277
+ "orgLevelOnly": "true", "max": 1000})
2278
+ items = result["virtualExtensions"] # response key: "virtualExtensions"
2279
+ # Each item: {id, displayName, firstName, lastName, extension, phoneNumber, locationId, locationName}
2280
+ ```
2281
+
2282
+ **Get Virtual Extension Details:**
2283
+
2284
+ ```python
2285
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensions/{ext_id}")
2286
+ ```
2287
+
2288
+ **Create Virtual Extension:**
2289
+
2290
+ ```python
2291
+ body = {
2292
+ "displayName": "Lobby Directory",
2293
+ "firstName": "Lobby",
2294
+ "lastName": "Directory",
2295
+ "extension": "9000",
2296
+ "phoneNumber": "+14155551000", # optional
2297
+ "locationId": loc_id # optional
2298
+ }
2299
+ result = api.session.rest_post(f"{BASE}/telephony/config/virtualExtensions", json=body)
2300
+ ext_id = result["id"]
2301
+ ```
2302
+
2303
+ **Update Virtual Extension:**
2304
+
2305
+ ```python
2306
+ api.session.rest_put(f"{BASE}/telephony/config/virtualExtensions/{ext_id}",
2307
+ json={"displayName": "New Name", "extension": "9001"})
2308
+ ```
2309
+
2310
+ **Delete Virtual Extension:**
2311
+
2312
+ ```python
2313
+ api.session.rest_delete(f"{BASE}/telephony/config/virtualExtensions/{ext_id}")
2314
+ ```
2315
+
2316
+ **Get Virtual Extension Settings** (org-level):
2317
+
2318
+ ```python
2319
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensions/settings")
2320
+ # Returns: mode (e.g., "STANDARD")
2321
+ ```
2322
+
2323
+ **Update Virtual Extension Settings:**
2324
+
2325
+ ```python
2326
+ api.session.rest_put(f"{BASE}/telephony/config/virtualExtensions/settings",
2327
+ json={"mode": "STANDARD"})
2328
+ ```
2329
+
2330
+ **Validate External Phone Numbers:**
2331
+
2332
+ ```python
2333
+ result = api.session.rest_post(
2334
+ f"{BASE}/telephony/config/virtualExtensions/actions/validateNumbers/invoke",
2335
+ json={"phoneNumbers": ["+14155551234"]})
2336
+ ```
2337
+
2338
+ #### Virtual Extension Ranges
2339
+
2340
+ **List Virtual Extension Ranges:**
2341
+
2342
+ ```python
2343
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensionRanges",
2344
+ params={"name": "Sales", "prefix": "90", "locationId": loc_id, "max": 1000})
2345
+ items = result["virtualExtensionRanges"] # response key: "virtualExtensionRanges"
2346
+ ```
2347
+
2348
+ **Get Virtual Extension Range Details:**
2349
+
2350
+ ```python
2351
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensionRanges/{range_id}")
2352
+ ```
2353
+
2354
+ **Create Virtual Extension Range:**
2355
+
2356
+ ```python
2357
+ result = api.session.rest_post(f"{BASE}/telephony/config/virtualExtensionRanges",
2358
+ json={"name": "Sales Range", "prefix": "90", "locationId": loc_id})
2359
+ range_id = result["id"]
2360
+ ```
2361
+
2362
+ **Update Virtual Extension Range:**
2363
+
2364
+ ```python
2365
+ api.session.rest_put(f"{BASE}/telephony/config/virtualExtensionRanges/{range_id}/",
2366
+ json={"name": "Updated Range", "prefix": "91", "action": "REPLACE"})
2367
+ ```
2368
+
2369
+ **Delete Virtual Extension Range:**
2370
+
2371
+ ```python
2372
+ api.session.rest_delete(f"{BASE}/telephony/config/virtualExtensionRanges/{range_id}")
2373
+ ```
2374
+
2375
+ **Validate Range Prefix:**
2376
+
2377
+ ```python
2378
+ result = api.session.rest_post(
2379
+ f"{BASE}/telephony/config/virtualExtensionRanges/actions/validate/invoke",
2380
+ json={"locationId": loc_id, "name": "Test Range", "prefix": "90"})
2381
+ ```
2382
+
2383
+ ### CLI Examples
2384
+
2385
+ #### Virtual Extensions
2386
+
2387
+ ```bash
2388
+ # List virtual extensions (org-wide, filterable)
2389
+ wxcli virtual-extensions list --location-id <loc_id>
2390
+
2391
+ # Get details for a virtual extension
2392
+ wxcli virtual-extensions show --virtual-extension-id <ext_id>
2393
+
2394
+ # Create a virtual extension
2395
+ wxcli virtual-extensions create --display-name "Lobby Directory" --extension "9000" --location-id <loc_id>
2396
+
2397
+ # Update a virtual extension
2398
+ wxcli virtual-extensions update --virtual-extension-id <ext_id> --display-name "New Name"
2399
+
2400
+ # Delete a virtual extension
2401
+ wxcli virtual-extensions delete --virtual-extension-id <ext_id>
2402
+
2403
+ # Get virtual extension settings (org-level)
2404
+ wxcli virtual-extensions show-settings
2405
+
2406
+ # Update virtual extension settings
2407
+ wxcli virtual-extensions update-settings --json-body '{"mode": "STANDARD"}'
2408
+
2409
+ # Validate external phone numbers
2410
+ wxcli virtual-extensions validate-an-external --json-body '{"phoneNumbers": ["+14155551234"]}'
2411
+ ```
2412
+
2413
+ #### Virtual Extension Ranges
2414
+
2415
+ ```bash
2416
+ # List virtual extension ranges
2417
+ wxcli virtual-extensions list-virtual-extension-ranges --location-id <loc_id>
2418
+
2419
+ # Get details for a virtual extension range
2420
+ wxcli virtual-extensions show-virtual-extension-ranges --virtual-extension-range-id <range_id>
2421
+
2422
+ # Create a virtual extension range
2423
+ wxcli virtual-extensions create-virtual-extension-ranges --name "Sales Range" --prefix "90" --location-id <loc_id>
2424
+
2425
+ # Update a virtual extension range
2426
+ wxcli virtual-extensions update-virtual-extension-ranges --virtual-extension-range-id <range_id> \
2427
+ --json-body '{"name": "Updated Range", "prefix": "91", "action": "REPLACE"}'
2428
+
2429
+ # Delete a virtual extension range
2430
+ wxcli virtual-extensions delete-virtual-extension-ranges --virtual-extension-range-id <range_id>
2431
+
2432
+ # Validate a range prefix
2433
+ wxcli virtual-extensions validate-the-prefix --json-body '{"locationId": "<loc_id>", "name": "Test Range", "prefix": "90"}'
2434
+ ```
2435
+
2436
+ ---
2437
+
2438
+ ## Data Models Quick Reference
2439
+
2440
+ ### Shared Models (from `wxc_sdk.common`)
2441
+
2442
+ | Model | Used By | Description |
2443
+ |-------|---------|-------------|
2444
+ | `PersonPlaceAgent` | Call Park, Call Pickup | Person/workspace agent with ID, name, phone, extension, type |
2445
+ | `CallParkExtension` | Call Park, Call Park Extensions | Lightweight model: id, name, extension |
2446
+ | `RingPattern` | Call Park Settings | Enum: ring pattern (e.g., `normal`) |
2447
+ | `UserType` | Paging | Enum for person vs. workspace |
2448
+ | `DirectLineCallerIdName` | Paging, Voicemail Groups | Caller ID name settings (replaces deprecated first/last name) |
2449
+ | `AvailableNumber` | Paging, Voicemail Groups | Available phone number for assignment |
2450
+ | `CallRecordingSetting` | Customer Assist (Queue Recording) | Full call recording configuration (see fields below) |
2451
+
2452
+ #### `CallRecordingSetting` Fields
2453
+
2454
+ | Field | Type | Notes |
2455
+ |-------|------|-------|
2456
+ | `enabled` | `bool` | Enable/disable call recording for the queue |
2457
+ | `record` | `str` | Recording mode: `Always`, `Never`, or `Always with Pause/Resume` |
2458
+ | `record_voicemail_enabled` | `bool` | Whether voicemail messages are recorded |
2459
+ | `start_stop_announcement_enabled` | `bool` | Play recording start/stop announcement to parties |
2460
+ | `notification` | `object` | Notification settings (type, threshold) |
2461
+ | `repeat` | `object` | Repeat notification settings (enabled, interval) |
2462
+ | `service_provider` | `str` | Recording vendor/provider name |
2463
+ | `external_group` | `str` | External group tag for recording categorization |
2464
+ | `external_identifier` | `str` | External identifier for the recording |
2465
+
2466
+ ### Feature-Specific Models
2467
+
2468
+ | Model | Module | Description |
2469
+ |-------|--------|-------------|
2470
+ | `Paging`, `PagingAgent` | `telephony.paging` | Paging group and its members |
2471
+ | `CallPark`, `CallParkSettings`, `LocationCallParkSettings` | `telephony.callpark` | Call park group and location-level settings |
2472
+ | `CallParkRecall`, `RecallHuntGroup`, `AvailableRecallHuntGroup` | `telephony.callpark` | Recall behavior when park times out |
2473
+ | `CallPickup`, `PickupNotificationType` | `telephony.callpickup` | Pickup group and notification enums |
2474
+ | `VoicemailGroup`, `VoicemailGroupDetail` | `telephony.voicemail_groups` | Voicemail group list/detail models |
2475
+ | `ScreenPopConfiguration` | `telephony.cx_essentials` | Screen pop settings |
2476
+ | `WrapUpReason`, `WrapUpReasonDetails`, `QueueWrapupReasonSettings` | `telephony.cx_essentials.wrapup_reasons` | Wrap-up reason models |
2477
+ | `AvailableQueue`, `WrapupReasonQueue`, `QueueSettingsReason` | `telephony.cx_essentials.wrapup_reasons` | Queue-related models for wrap-up |
2478
+
2479
+ ---
2480
+
2481
+ ## Dependencies & Relationships
2482
+
2483
+ ```
2484
+ Call Park ──────────── Call Park Extensions (assigned to park groups)
2485
+ |
2486
+ +──── Agents (PersonPlaceAgent: people, workspaces)
2487
+ |
2488
+ +──── Recall Hunt Group (optional: fallback when park times out)
2489
+
2490
+ Call Pickup ─────────── Agents (PersonPlaceAgent: people, workspaces, virtual lines)
2491
+
2492
+ Paging Group ────────── Originators (PagingAgent: who can page)
2493
+ |
2494
+ +──── Targets (PagingAgent: who receives pages, up to 75)
2495
+
2496
+ Voicemail Group ──────── Assigned to auto attendants, call queues, hunt groups
2497
+ |
2498
+ +──── Available Numbers (for primary and fax)
2499
+
2500
+ Customer Assist ─────── Call Queues (screen pop, recording, wrap-up per queue)
2501
+ |
2502
+ +──── Wrap-Up Reasons (org-level, assigned to queues)
2503
+ |
2504
+ +──── Available Agents (Customer Assist or Basic licensed)
2505
+ ```
2506
+
2507
+ ### Key Dependency Notes
2508
+
2509
+ 1. **Call Park requires Call Park Extensions**: You create Call Park Extensions first, then assign them to Call Park groups.
2510
+ 2. **Call Park recall requires Hunt Groups**: If using `ALERT_PARKING_USER_FIRST_THEN_HUNT_GROUP` or `ALERT_HUNT_GROUP_ONLY`, a Hunt Group must exist at the location.
2511
+ 3. **Customer Assist requires Call Queues**: Screen pop, queue recording, and wrap-up reasons are all per-queue configurations. Call queues must be created first.
2512
+ 4. **Customer Assist requires licensing**: The `has_cx_essentials` filter on `available_agents()` distinguishes between Customer Assist and CX Basic licensed agents.
2513
+ 5. **Voicemail Groups are location-scoped**: Created within a location, but listed org-wide.
2514
+ 6. **Paging Groups are location-scoped**: Created within a location, but can be listed org-wide.
2515
+ 7. **ID instability for Call Park and Call Pickup**: The IDs for Call Park and Call Pickup entities change when their names are modified. Always re-fetch the ID after a name change.
2516
+
2517
+ ---
2518
+
2519
+ ## Gotchas (Cross-Cutting)
2520
+
2521
+ - **ID instability**: Call Park and Call Pickup IDs change when the entity name is modified. Always re-fetch the ID after a name update.
2522
+ - **Agent format differs by feature type** : Hunt Groups and Call Queues take `agents` as `[{"id": "person_id"}]` (array of objects). Call Pickups take `agents` as `["person_id"]` (plain string array). Paging Groups take `targets` and `originators` as plain string arrays. Using `[{"id": ...}]` for pickup or paging returns 400 "Invalid field value: agents/targets". Reads always return full agent objects regardless.
2523
+ - **Location-scoped features listed org-wide**: Paging Groups and Voicemail Groups can be listed org-wide (no `locationId` required), but **Call Parks and Call Pickups require `locationId`** for list operations. `wxcli call-park list` without a location argument returns empty. Must enumerate per-location during cleanup.
2524
+ - **Nested settings require `--json-body`**: Features with complex nested body fields (voicemail group create, call park recall, screen pop, wrap-up settings) need `--json-body` in the CLI because the generator skips deeply nested object/array fields.
2525
+ - **Voicemail Group create is strict**: Requires 7+ fields (name, extension, passcode, languageCode, messageStorage, notifications, faxMessage, transferToNumber, emailCopyOfMessage). The wxc_sdk `VoicemailGroupDetail.for_create()` has a bug (missing `by_alias=True`); use `--json-body` via CLI or `model_dump(by_alias=True)` via SDK.
2526
+ - **Customer Assist requires licensing**: Screen pop, queue recording, and wrap-up reasons require Customer Assist licensing. Call queues must exist before configuring these features. Error 28018 ("CX Essentials is not enabled for this Call center") means the target queue is not a Customer Assist queue.
2527
+ - **CX queue creation requires `callPolicies`**: Creating a Customer Assist queue without `callPolicies` in the request body returns 400. Use `--json-body` with at minimum `{"callPolicies":{"policy":"SIMULTANEOUS"}}`.
2528
+ - **CX queues hidden from default list**: `wxcli call-queue list` does not show Customer Assist queues. Pass `--has-cx-essentials true` to see them.
2529
+ - **Supervisor delete returns 204 but persists**: `delete-supervisors-config-1 --has-cx-essentials true` gets 204 from the API but the supervisor remains. Workaround: use `update-supervisors` with `action: DELETE` on each agent — removing the last agent auto-removes the supervisor.
2530
+ - **Call Park requires recall**: Creating a Call Park without a `recall` option (e.g., `ALERT_PARKING_USER_ONLY`) will be rejected by the API.
2531
+ - **Announcement upload requires multipart/form-data**: The CLI and raw HTTP `rest_post` may not support binary file uploads directly. Use the SDK upload methods or construct multipart requests manually.
2532
+ - **CallPickupGroup AXL creation with members fails on CUCM 15.0.** The `addCallPickupGroup` AXL operation with `<members>` containing `<directoryNumber>` elements fails with a null priority foreign key constraint (`pickupgroupmember.priority`). Workaround: create the pickup group empty, then add members via `updateLine` with `callPickupGroupName` on each member DN. Verified on CUCM 15.0.1.13901(2).
2533
+ - **No native PagingGroup AXL object type.** CUCM does not expose paging groups through AXL (`listPagingGroup`/`getPagingGroup` do not exist). Paging requires third-party systems (InformaCast, Cisco Paging Server). The migration pipeline's `CanonicalPagingGroup` type exists for manual/CSV import but cannot be auto-extracted from CUCM.
2534
+
2535
+ ---
2536
+
2537
+ ## See Also
2538
+
2539
+ - [Major Call Features](call-features-major.md) -- Auto Attendants, Call Queues, and Hunt Groups (Call Park recall references Hunt Groups; Customer Assist extends Call Queues; Voicemail Groups can be assigned to AA/CQ/HG)
2540
+ - [Call Routing & PSTN](call-routing.md) -- dial plans, trunks, and routing chain (Paging Groups and Voicemail Groups with phone numbers participate in call routing)
2541
+ - [Provisioning Reference](provisioning.md) -- creating locations and users (all features in this doc are location-scoped and require existing locations)