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,1132 @@
1
+ <!-- Updated by playbook session 2026-03-18 -->
2
+
3
+ # Virtual Lines & Virtual Extensions
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Overview](#overview)
8
+ 2. [Virtual Lines](#virtual-lines)
9
+ 3. [Virtual Extensions](#virtual-extensions)
10
+ 4. [Raw HTTP](#raw-http)
11
+ 5. [Virtual Lines vs. Virtual Extensions: Decision Guide](#virtual-lines-vs-virtual-extensions-decision-guide)
12
+ 6. [Source](#source)
13
+ 7. [See Also](#see-also)
14
+
15
+ ## Overview
16
+
17
+ Webex Calling provides two distinct "virtual" constructs for phone number/extension management:
18
+
19
+ | Concept | What It Is | Primary Use Case |
20
+ |---------|-----------|-----------------|
21
+ | **Virtual Line** | A fully-featured phone line not tied to a physical user | Shared department lines, lobby phones, general-purpose numbers that need call settings (voicemail, forwarding, recording, etc.) |
22
+ | **Virtual Extension** | An extension that maps to an external phone number | Simplified dialing to remote workers or frequently-called external numbers on a separate telephony system |
23
+
24
+ These are **not interchangeable**. A virtual line lives inside Webex Calling with its own call settings. A virtual extension is a routing alias that forwards to an external PSTN number.
25
+
26
+ ---
27
+
28
+ ## Virtual Lines
29
+
30
+ ### What They Are
31
+
32
+ A virtual line is a phone line within Webex Calling that is not assigned to a specific person. It has its own phone number and/or extension, and supports the full range of call settings that a person line does (voicemail, call forwarding, call recording, caller ID, etc.).
33
+
34
+ **Common use cases:**
35
+ - Shared department lines (e.g., `+1-555-SALES` rings a group)
36
+ - Lobby or front-desk phones
37
+ - Lines assigned to devices in common areas that need independent call handling
38
+ - Overflow or backup lines with their own voicemail
39
+
40
+ **Key characteristics:**
41
+ - Must be created within a specific location
42
+ - Can have a phone number, an extension, or both (at least one is required)
43
+ - Can be assigned to physical devices (phones, DECT handsets)
44
+ - Supports all person-level call settings (voicemail, forwarding, DND, etc.)
45
+ - Appears in directory search (configurable)
46
+ - Has its own caller ID settings
47
+ - Has its own time zone and announcement language
48
+
49
+ ### SDK Access
50
+
51
+ ```python
52
+ # Virtual Lines API is at:
53
+ api = wxc_api.telephony.virtual_lines # VirtualLinesApi instance
54
+ ```
55
+
56
+ Base path: `telephony/config/virtualLines`
57
+
58
+ Required scopes:
59
+ - **Read**: `spark-admin:telephony_config_read`
60
+ - **Write**: `spark-admin:telephony_config_write`
61
+
62
+ ### CRUD Operations
63
+
64
+ #### List Virtual Lines
65
+
66
+ ```python
67
+ VirtualLinesApi.list(
68
+ org_id: str = None,
69
+ location_id: list[str] = None, # Filter by location IDs
70
+ id: list[str] = None, # Filter by virtual line IDs
71
+ owner_name: list[str] = None, # Filter by owner name
72
+ phone_number: list[str] = None, # Filter by phone number
73
+ location_name: list[str] = None, # Filter by location name
74
+ order: list[str] = None, # Sort order (max 3 fields)
75
+ has_device_assigned: bool = None, # Only with/without devices
76
+ has_extension_assigned: bool = None, # Only with/without extension
77
+ has_dn_assigned: bool = None, # Only with/without directory number
78
+ ) -> Generator[VirtualLine, None, None]
79
+ ```
80
+
81
+ Returns a paginated generator of `VirtualLine` objects. Supports multiple filter values per parameter (e.g., multiple `location_id` values).
82
+
83
+ **Example:**
84
+ ```python
85
+ # List all virtual lines at a location
86
+ for vl in api.telephony.virtual_lines.list(location_id=["loc_id_123"]):
87
+ print(f"{vl.display_name}: {vl.number}")
88
+ ```
89
+
90
+ #### Create Virtual Line
91
+
92
+ ```python
93
+ VirtualLinesApi.create(
94
+ first_name: str, # Required, 1-30 chars
95
+ last_name: str, # Required, 1-30 chars
96
+ location_id: str, # Required
97
+ display_name: str = None,
98
+ phone_number: str = None, # 1-23 chars; phone_number OR extension required
99
+ extension: str = None, # 2-10 chars; phone_number OR extension required
100
+ caller_id_last_name: str = None, # 1-30 chars
101
+ caller_id_first_name: str = None, # 1-30 chars
102
+ caller_id_number: str = None, # 1-23 chars
103
+ org_id: str = None,
104
+ ) -> str # Returns the new virtual line ID
105
+ ```
106
+
107
+ At least one of `phone_number` or `extension` must be provided.
108
+
109
+ **Example:**
110
+ ```python
111
+ vl_id = api.telephony.virtual_lines.create(
112
+ first_name="Sales",
113
+ last_name="Line",
114
+ location_id="loc_id_123",
115
+ extension="8500",
116
+ phone_number="+15551234567"
117
+ )
118
+ ```
119
+
120
+ #### Get Virtual Line Details
121
+
122
+ ```python
123
+ VirtualLinesApi.details(
124
+ virtual_line_id: str, # Required
125
+ org_id: str = None,
126
+ ) -> VirtualLine
127
+ ```
128
+
129
+ Returns the full `VirtualLine` object including location, number, devices, caller ID, time zone, and announcement language.
130
+
131
+ #### Update Virtual Line
132
+
133
+ ```python
134
+ VirtualLinesApi.update(
135
+ virtual_line_id: str, # Required
136
+ first_name: str = None,
137
+ last_name: str = None,
138
+ display_name: str = None,
139
+ phone_number: str = None,
140
+ extension: str = None,
141
+ announcement_language: str = None,
142
+ caller_id_last_name: str = None,
143
+ caller_id_first_name: str = None,
144
+ caller_id_number: str = None,
145
+ time_zone: str = None,
146
+ org_id: str = None,
147
+ ) -> None
148
+ ```
149
+
150
+ Only include fields you want to change. Omitted fields are not modified.
151
+
152
+ #### Delete Virtual Line
153
+
154
+ ```python
155
+ VirtualLinesApi.delete(
156
+ virtual_line_id: str, # Required
157
+ org_id: str = None,
158
+ ) -> None
159
+ ```
160
+
161
+ ### Additional Virtual Line Operations
162
+
163
+ #### Get Phone Number
164
+
165
+ ```python
166
+ VirtualLinesApi.get_phone_number(
167
+ virtual_line_id: str,
168
+ org_id: str = None,
169
+ ) -> VirtualLineNumberPhoneNumber
170
+ ```
171
+
172
+ Returns the `direct_number`, `extension`, and `primary` flag for the assigned phone number.
173
+
174
+ #### Update Directory Search
175
+
176
+ ```python
177
+ VirtualLinesApi.update_directory_search(
178
+ virtual_line_id: str,
179
+ enabled: bool, # Whether the virtual line appears in directory search
180
+ org_id: str = None,
181
+ ) -> None
182
+ ```
183
+
184
+ #### Get Assigned Devices
185
+
186
+ ```python
187
+ VirtualLinesApi.assigned_devices(
188
+ virtual_line_id: str,
189
+ org_id: str = None,
190
+ ) -> VirtualLineDevices
191
+ ```
192
+
193
+ Returns the list of devices assigned to the virtual line, the available endpoint type (`PrimaryOrShared`), and the maximum device count.
194
+
195
+ #### Get DECT Network Handsets
196
+
197
+ ```python
198
+ VirtualLinesApi.dect_networks(
199
+ virtual_line_id: str,
200
+ org_id: str = None,
201
+ ) -> list[AssignedDectNetwork]
202
+ ```
203
+
204
+ Returns DECT network handset assignments for the virtual line.
205
+
206
+ ### Virtual Line Call Settings
207
+
208
+ Virtual lines support the same call settings as person lines. Each is accessed as a sub-API on the `VirtualLinesApi` instance:
209
+
210
+ | Sub-API | Attribute | What It Controls |
211
+ |---------|-----------|-----------------|
212
+ | `AgentCallerIdApi` | `.agent_caller_id` | Caller ID when acting as a call queue/hunt group agent |
213
+ | `AvailableNumbersApi` | `.available_numbers` | List available numbers for assignment |
214
+ | `BargeApi` | `.barge` | Barge-in settings |
215
+ | `CallBridgeApi` | `.call_bridge` | Call bridge settings |
216
+ | `CallInterceptApi` | `.call_intercept` | Call intercept (redirect/block incoming calls) |
217
+ | `CallRecordingApi` | `.call_recording` | Call recording settings |
218
+ | `CallWaitingApi` | `.call_waiting` | Call waiting settings |
219
+ | `CallerIdApi` | `.caller_id` | Outbound caller ID configuration |
220
+ | `DndApi` | `.dnd` | Do Not Disturb settings |
221
+ | `ECBNApi` | `.ecbn` | Emergency callback number |
222
+ | `PersonForwardingApi` | `.forwarding` | Call forwarding rules (always, busy, no-answer) |
223
+ | `MusicOnHoldApi` | `.music_on_hold` | Music on hold settings |
224
+ | `IncomingPermissionsApi` | `.permissions_in` | Incoming call permissions |
225
+ | `OutgoingPermissionsApi` | `.permissions_out` | Outgoing call permissions |
226
+ | `PrivacyApi` | `.privacy` | Privacy settings |
227
+ | `PushToTalkApi` | `.push_to_talk` | Push-to-talk settings |
228
+ | `VoicemailApi` | `.voicemail` | Voicemail settings (greeting, PIN, notifications, etc.) |
229
+
230
+ All sub-APIs use the virtual line ID as the entity identifier, and the URL pattern is:
231
+ ```
232
+ telephony/config/virtualLines/{virtual_line_id}/{feature}
233
+ ```
234
+
235
+ **Example -- read voicemail settings for a virtual line:**
236
+ ```python
237
+ vm_settings = api.telephony.virtual_lines.voicemail.read(entity_id=vl_id)
238
+ ```
239
+
240
+ **Example -- configure call forwarding for a virtual line:**
241
+ ```python
242
+ api.telephony.virtual_lines.forwarding.configure(entity_id=vl_id, ...)
243
+ ```
244
+
245
+ ### CLI Examples
246
+
247
+ The `virtual-line-settings` command group covers virtual line CRUD and all call settings (63 commands total). The commands mirror the person settings commands in `user-settings`.
248
+
249
+ #### Virtual Line CRUD
250
+
251
+ ```bash
252
+ # List all virtual lines
253
+ wxcli virtual-line-settings list
254
+
255
+ # List virtual lines at a specific location
256
+ wxcli virtual-line-settings list --location-id Y2lzY29zcGFyazovL3VzL0xPQ0FUSU9OLzU2Nzg=
257
+
258
+ # List virtual lines with filters
259
+ wxcli virtual-line-settings list --owner-name "Sales" --has-device-assigned true
260
+
261
+ # Show details for a virtual line
262
+ wxcli virtual-line-settings show-virtual-lines Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
263
+
264
+ # Create a virtual line (firstName, lastName, locationId required; phoneNumber or extension required)
265
+ wxcli virtual-line-settings create \
266
+ --first-name "Sales" --last-name "Line" \
267
+ --location-id Y2lzY29zcGFyazovL3VzL0xPQ0FUSU9OLzU2Nzg= \
268
+ --extension 8500 --phone-number "+15551234567"
269
+
270
+ # Create with caller ID overrides
271
+ wxcli virtual-line-settings create \
272
+ --first-name "Front" --last-name "Desk" \
273
+ --location-id Y2lzY29zcGFyazovL3VzL0xPQ0FUSU9OLzU2Nzg= \
274
+ --extension 8000 \
275
+ --caller-id-first-name "Main" --caller-id-last-name "Office" \
276
+ --caller-id-number "+15559999999"
277
+
278
+ # Update a virtual line
279
+ wxcli virtual-line-settings update-virtual-lines Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
280
+ --first-name "Sales" --last-name "Main Line" --extension 8501
281
+
282
+ # Update time zone and announcement language
283
+ wxcli virtual-line-settings update-virtual-lines Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
284
+ --time-zone "America/Chicago" --announcement-language "en_us"
285
+
286
+ # Delete a virtual line
287
+ wxcli virtual-line-settings delete Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
288
+
289
+ # Delete without confirmation prompt
290
+ wxcli virtual-line-settings delete Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 --force
291
+
292
+ # Get phone number assigned to a virtual line
293
+ wxcli virtual-line-settings show-number Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
294
+
295
+ # List devices assigned to a virtual line
296
+ wxcli virtual-line-settings list-devices Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
297
+ ```
298
+
299
+ #### Call Handling Settings
300
+
301
+ ```bash
302
+ # Read call forwarding settings
303
+ wxcli virtual-line-settings show-call-forwarding Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
304
+
305
+ # Enable always-forward (nested settings require --json-body)
306
+ wxcli virtual-line-settings update-call-forwarding Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
307
+ --json-body '{"callForwarding":{"always":{"enabled":true,"destination":"+15551234567","ringReminderEnabled":true}}}'
308
+
309
+ # Enable no-answer forwarding with 5 rings
310
+ wxcli virtual-line-settings update-call-forwarding Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
311
+ --json-body '{"callForwarding":{"noAnswer":{"enabled":true,"destination":"+15556667777","numberOfRings":5}}}'
312
+
313
+ # Enable business continuity forwarding
314
+ wxcli virtual-line-settings update-call-forwarding Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
315
+ --json-body '{"businessContinuity":{"enabled":true,"destination":"+18889990000"}}'
316
+
317
+ # Read call waiting settings
318
+ wxcli virtual-line-settings show-call-waiting Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
319
+
320
+ # Enable call waiting
321
+ wxcli virtual-line-settings update-call-waiting Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 --enabled
322
+
323
+ # Disable call waiting
324
+ wxcli virtual-line-settings update-call-waiting Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 --no-enabled
325
+
326
+ # Read DND settings
327
+ wxcli virtual-line-settings show-do-not-disturb Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
328
+
329
+ # Enable DND with ring splash
330
+ wxcli virtual-line-settings update-do-not-disturb Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
331
+ --enabled --ring-splash-enabled
332
+
333
+ # Disable DND
334
+ wxcli virtual-line-settings update-do-not-disturb Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 --no-enabled
335
+ ```
336
+
337
+ #### Voicemail & Media Settings
338
+
339
+ ```bash
340
+ # Read voicemail settings
341
+ wxcli virtual-line-settings show-voicemail Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
342
+
343
+ # Enable voicemail with default greetings (nested settings require --json-body)
344
+ wxcli virtual-line-settings update-voicemail Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
345
+ --json-body '{"enabled":true,"sendBusyCalls":{"enabled":true,"greeting":"DEFAULT"},"sendUnansweredCalls":{"enabled":true,"greeting":"DEFAULT","numberOfRings":3}}'
346
+
347
+ # Enable voicemail with simple flag (just enable/disable)
348
+ wxcli virtual-line-settings update-voicemail Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 --enabled
349
+
350
+ # Read caller ID settings
351
+ wxcli virtual-line-settings list-caller-id Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
352
+
353
+ # Update caller ID to use direct line
354
+ wxcli virtual-line-settings update-caller-id-virtual-lines Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
355
+ --selected DIRECT_LINE
356
+
357
+ # Update caller ID to use a custom number
358
+ wxcli virtual-line-settings update-caller-id-virtual-lines Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
359
+ --selected CUSTOM --custom-number "+15559999999" \
360
+ --first-name "Sales" --last-name "Department"
361
+
362
+ # Read call recording settings
363
+ wxcli virtual-line-settings show Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
364
+
365
+ # Enable call recording (always record)
366
+ wxcli virtual-line-settings update Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
367
+ --enabled --record "Always"
368
+
369
+ # Enable call recording with voicemail recording
370
+ wxcli virtual-line-settings update Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 \
371
+ --enabled --record "Always" --record-voicemail-enabled
372
+ ```
373
+
374
+ #### Permissions & Other Settings
375
+
376
+ ```bash
377
+ # Read incoming permission settings
378
+ wxcli virtual-line-settings show-incoming-permission Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
379
+
380
+ # Read outgoing permission settings
381
+ wxcli virtual-line-settings list-outgoing-permission Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
382
+
383
+ # Read barge-in settings
384
+ wxcli virtual-line-settings show-barge-in Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
385
+
386
+ # Read call intercept settings
387
+ wxcli virtual-line-settings show-intercept Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
388
+
389
+ # Read music on hold settings
390
+ wxcli virtual-line-settings show-music-on-hold Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
391
+
392
+ # List DECT network handsets assigned to a virtual line
393
+ wxcli virtual-line-settings list-dect-networks Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0
394
+
395
+ # Enable directory search visibility
396
+ wxcli virtual-line-settings update-directory-search Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 --enabled
397
+
398
+ # Disable directory search visibility
399
+ wxcli virtual-line-settings update-directory-search Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfTElORS8xMjM0 --no-enabled
400
+ ```
401
+
402
+ > **Note:** The `show` and `update` commands in `virtual-line-settings` map to call recording settings (not virtual line details). Use `show-virtual-lines` and `update-virtual-lines` for virtual line details.
403
+
404
+ ### Data Models
405
+
406
+ #### VirtualLine
407
+
408
+ ```python
409
+ class VirtualLine(ApiModel):
410
+ id: Optional[str] # Unique identifier
411
+ first_name: Optional[str] # First name (1-30 chars)
412
+ last_name: Optional[str] # Last name (1-30 chars)
413
+ display_name: Optional[str] # Display name
414
+ caller_id_first_name: Optional[str] # CLID first name
415
+ caller_id_last_name: Optional[str] # CLID last name
416
+ caller_id_number: Optional[str] # CLID phone number
417
+ external_caller_id_name_policy: Optional[ExternalCallerIdNamePolicy]
418
+ custom_external_caller_id_name: Optional[str]
419
+ number: Optional[UserNumber] # Phone number + extension
420
+ location: Optional[VirtualLineLocation] # Location details
421
+ number_of_devices_assigned: Optional[int]
422
+ billing_plan: Optional[str]
423
+ directory_search_enabled: Optional[bool]
424
+ announcement_language: Optional[str]
425
+ time_zone: Optional[str]
426
+ devices: Optional[list[TelephonyDevice]] # Assigned devices
427
+ ```
428
+
429
+ #### VirtualLineLocation
430
+
431
+ ```python
432
+ class VirtualLineLocation(ApiModel):
433
+ id: Optional[str] # Location ID
434
+ name: Optional[str] # Location name
435
+ address: Optional[LocationAddress] # Physical address
436
+ ```
437
+
438
+ #### VirtualLineNumberPhoneNumber
439
+
440
+ ```python
441
+ class VirtualLineNumberPhoneNumber(ApiModel):
442
+ direct_number: Optional[str] # Assigned phone number
443
+ extension: Optional[str] # Assigned extension
444
+ primary: Optional[bool] # Whether this is the primary number
445
+ ```
446
+
447
+ #### VirtualLineDevices
448
+
449
+ ```python
450
+ class VirtualLineDevices(ApiModel):
451
+ devices: Optional[list[TelephonyDevice]] # Assigned devices
452
+ available_endpoint_type: Optional[PrimaryOrShared] # Primary or shared line
453
+ max_device_count: Optional[int] # Max devices allowed
454
+ ```
455
+
456
+ ---
457
+
458
+ ## Virtual Extensions
459
+
460
+ ### What They Are
461
+
462
+ Virtual extensions are **different from virtual lines**. A virtual extension maps an internal extension number to an external phone number (E.164 format). This enables users to dial a short extension to reach someone on a separate telephony system or an external number.
463
+
464
+ **Common use cases:**
465
+ - Remote workers on a different phone system who need to be reachable by extension
466
+ - Frequently-called external contacts (clients, vendors) assigned a speed-dial extension
467
+ - Branch offices on a separate PBX that you want integrated into the Webex Calling dial plan
468
+
469
+ **Key characteristics:**
470
+ - Can be defined at the **organization level** (reachable from all locations) or the **location level** (reachable by extension within that location, or by ESN from other locations)
471
+ - Maps an extension to an external E.164 phone number
472
+ - Two operating modes: **Standard** (default, requires E.164 prefix) and **Enhanced** (requires PSTN provider support for special signaling)
473
+ - Supports wildcard patterns via virtual extension ranges
474
+
475
+ ### SDK Access
476
+
477
+ ```python
478
+ # Virtual Extensions API is at:
479
+ api = wxc_api.telephony.virtual_extensions # VirtualExtensionsApi instance
480
+ ```
481
+
482
+ Base path: `telephony/config`
483
+
484
+ Required scopes:
485
+ - **Read**: `spark-admin:telephony_config_read`
486
+ - **Write**: `spark-admin:telephony_config_write` (and `identity:contacts_rw` for create/update/delete of individual extensions)
487
+
488
+ ### Individual Virtual Extensions
489
+
490
+ #### List Virtual Extensions
491
+
492
+ ```python
493
+ VirtualExtensionsApi.list_extensions(
494
+ order: str = None, # ASC or DSC
495
+ extension: str = None, # Filter by extension number
496
+ phone_number: str = None, # Filter by phone number
497
+ name: str = None, # Filter by first or last name
498
+ location_name: str = None, # Filter by location name
499
+ location_id: str = None, # Filter by location ID
500
+ org_level_only: bool = None, # Only org-level extensions
501
+ org_id: str = None,
502
+ ) -> Generator[VirtualExtension, None, None]
503
+ ```
504
+
505
+ Note: Only one of `location_name`, `location_id`, and `org_level_only` is allowed at the same time.
506
+
507
+ #### Create Virtual Extension
508
+
509
+ ```python
510
+ VirtualExtensionsApi.create_extension(
511
+ display_name: str, # Required
512
+ phone_number: str, # Required, E.164 external number
513
+ extension: str, # Required
514
+ first_name: str = None,
515
+ last_name: str = None,
516
+ location_id: str = None, # Omit for org-level
517
+ org_id: str = None,
518
+ ) -> str # Returns the new virtual extension ID
519
+ ```
520
+
521
+ - If `location_id` is omitted, the virtual extension is created at the **organization level** (reachable from all locations).
522
+ - If `location_id` is provided, it is a **location-level** extension (reachable as a local extension within that location; other locations use the ESN).
523
+
524
+ **Example:**
525
+ ```python
526
+ ve_id = api.telephony.virtual_extensions.create_extension(
527
+ display_name="Alice Remote",
528
+ phone_number="+15559876543",
529
+ extension="7001",
530
+ first_name="Alice",
531
+ last_name="Remote"
532
+ )
533
+ ```
534
+
535
+ #### Get Virtual Extension Details
536
+
537
+ ```python
538
+ VirtualExtensionsApi.details_extension(
539
+ extension_id: str, # Required
540
+ org_id: str = None,
541
+ ) -> VirtualExtension
542
+ ```
543
+
544
+ #### Update Virtual Extension
545
+
546
+ ```python
547
+ VirtualExtensionsApi.update_extension(
548
+ extension_id: str, # Required
549
+ first_name: str = None,
550
+ last_name: str = None,
551
+ display_name: str = None,
552
+ phone_number: str = None,
553
+ extension: str = None,
554
+ org_id: str = None,
555
+ ) -> None
556
+ ```
557
+
558
+ #### Delete Virtual Extension
559
+
560
+ ```python
561
+ VirtualExtensionsApi.delete_extension(
562
+ extension_id: str, # Required
563
+ org_id: str = None,
564
+ ) -> None
565
+ ```
566
+
567
+ #### Validate External Phone Numbers
568
+
569
+ ```python
570
+ VirtualExtensionsApi.validate_external_phone_number(
571
+ phone_numbers: list[str], # List of E.164 numbers to validate
572
+ org_id: str = None,
573
+ ) -> ValidatePhoneNumber
574
+ ```
575
+
576
+ Pre-check that external numbers are properly formatted, eligible, and not already in use before assigning them as virtual extensions.
577
+
578
+ ### CLI Examples
579
+
580
+ The `virtual-extensions` command group covers virtual extension CRUD, ranges, settings, and validation (14 commands).
581
+
582
+ #### Individual Virtual Extensions
583
+
584
+ ```bash
585
+ # List all virtual extensions
586
+ wxcli virtual-extensions list
587
+
588
+ # List virtual extensions filtered by location
589
+ wxcli virtual-extensions list --location-id Y2lzY29zcGFyazovL3VzL0xPQ0FUSU9OLzU2Nzg=
590
+
591
+ # List org-level virtual extensions only
592
+ wxcli virtual-extensions list --org-level-only true
593
+
594
+ # Filter by name or extension number
595
+ wxcli virtual-extensions list --name "Alice" --extension 7001
596
+
597
+ # Show details for a virtual extension
598
+ wxcli virtual-extensions show Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfRVhULzEyMzQ=
599
+
600
+ # Create a virtual extension (displayName, phoneNumber, extension required)
601
+ wxcli virtual-extensions create \
602
+ --display-name "Alice Remote" --phone-number "+15559876543" --extension 7001 \
603
+ --first-name "Alice" --last-name "Remote"
604
+
605
+ # Create a location-level virtual extension
606
+ wxcli virtual-extensions create \
607
+ --display-name "Branch PBX" --phone-number "+15551112222" --extension 7050 \
608
+ --location-id Y2lzY29zcGFyazovL3VzL0xPQ0FUSU9OLzU2Nzg=
609
+
610
+ # Update a virtual extension
611
+ wxcli virtual-extensions update Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfRVhULzEyMzQ= \
612
+ --display-name "Alice Remote (Updated)" --phone-number "+15559876544"
613
+
614
+ # Delete a virtual extension
615
+ wxcli virtual-extensions delete Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfRVhULzEyMzQ=
616
+
617
+ # Delete without confirmation prompt
618
+ wxcli virtual-extensions delete Y2lzY29zcGFyazovL3VzL1ZJUlRVQUxfRVhULzEyMzQ= --force
619
+
620
+ # Validate external phone numbers before creating extensions
621
+ wxcli virtual-extensions validate-an-external \
622
+ --json-body '{"phoneNumbers":["+15551112222","+15553334444"]}'
623
+ ```
624
+
625
+ #### Extension Settings (Mode)
626
+
627
+ ```bash
628
+ # Show current virtual extension mode (STANDARD or ENHANCED)
629
+ wxcli virtual-extensions show-settings
630
+
631
+ # Update virtual extension mode
632
+ wxcli virtual-extensions update-settings --mode STANDARD
633
+ ```
634
+
635
+ #### Virtual Extension Ranges
636
+
637
+ ```bash
638
+ # List all virtual extension ranges
639
+ wxcli virtual-extensions list-virtual-extension-ranges
640
+
641
+ # Show details for a range
642
+ wxcli virtual-extensions show-virtual-extension-ranges Y2lzY29zcGFyazovL3VzL1JBTkdFLzEyMzQ=
643
+
644
+ # Create a range with wildcard patterns (patterns via --json-body)
645
+ wxcli virtual-extensions create-virtual-extension-ranges \
646
+ --name "Remote Office Block" --prefix "+15559870000" \
647
+ --json-body '{"patterns":["70XX","71XX"]}'
648
+
649
+ # Create a location-level range
650
+ wxcli virtual-extensions create-virtual-extension-ranges \
651
+ --name "Branch Block" --prefix "+15559870000" \
652
+ --location-id Y2lzY29zcGFyazovL3VzL0xPQ0FUSU9OLzU2Nzg= \
653
+ --json-body '{"patterns":["80XX"]}'
654
+
655
+ # Update a range (add new patterns)
656
+ wxcli virtual-extensions update-virtual-extension-ranges Y2lzY29zcGFyazovL3VzL1JBTkdFLzEyMzQ= \
657
+ --action ADD --json-body '{"patterns":["72XX"]}'
658
+
659
+ # Delete a range
660
+ wxcli virtual-extensions delete-virtual-extension-ranges Y2lzY29zcGFyazovL3VzL1JBTkdFLzEyMzQ=
661
+
662
+ # Validate a range before creating
663
+ wxcli virtual-extensions validate-the-prefix \
664
+ --json-body '{"name":"Test Range","prefix":"+15559870000","patterns":["70XX"]}'
665
+ ```
666
+
667
+ ### Virtual Extension Ranges
668
+
669
+ Virtual extension ranges let you define patterns with wildcards to route blocks of extensions to external prefixes. For example, you can route extensions `7000`-`7099` to an external prefix in a single range definition instead of creating 100 individual virtual extensions.
670
+
671
+ #### List Ranges
672
+
673
+ ```python
674
+ VirtualExtensionsApi.list_range(
675
+ order: str = None, # Sort by name or prefix (ASC/DSC)
676
+ name: str = None, # Filter by range name
677
+ prefix: str = None, # Filter by prefix
678
+ location_id: str = None, # Filter by location ID
679
+ org_level_only: bool = None, # Only org-level ranges
680
+ org_id: str = None,
681
+ ) -> Generator[VirtualExtensionRange, None, None]
682
+ ```
683
+
684
+ Only one of `location_id` and `org_level_only` is allowed at the same time.
685
+
686
+ #### Create Range
687
+
688
+ ```python
689
+ VirtualExtensionsApi.create_range(
690
+ name: str, # Required, unique name
691
+ prefix: str, # Required, E.164 in Standard mode
692
+ patterns: list[str] = None, # Up to 100 patterns, wildcards "X" allowed
693
+ location_id: str = None, # Omit for org-level
694
+ org_id: str = None,
695
+ ) -> str # Returns the new range ID
696
+ ```
697
+
698
+ **Pattern wildcards:** Extension patterns can include one or more right-justified `X` characters matching any digit. For example, `70XX` matches extensions 7000-7099.
699
+
700
+ **Example:**
701
+ ```python
702
+ range_id = api.telephony.virtual_extensions.create_range(
703
+ name="Remote Office Block",
704
+ prefix="+15559870000",
705
+ patterns=["70XX", "71XX"]
706
+ )
707
+ ```
708
+
709
+ #### Get Range Details
710
+
711
+ ```python
712
+ VirtualExtensionsApi.details_range(
713
+ extension_range_id: str,
714
+ org_id: str = None,
715
+ ) -> VirtualExtensionRange
716
+ ```
717
+
718
+ #### Modify Range
719
+
720
+ ```python
721
+ VirtualExtensionsApi.modify_range(
722
+ extension_range_id: str, # Required
723
+ name: str = None,
724
+ prefix: str = None,
725
+ patterns: list[str] = None, # Max 100 patterns per request
726
+ action: VirtualExtensionRangeAction = None, # ADD, REMOVE, or REPLACE
727
+ org_id: str = None,
728
+ ) -> None
729
+ ```
730
+
731
+ The `action` parameter is **mandatory** when `patterns` are provided:
732
+ - `ADD` -- add new patterns to the existing range
733
+ - `REMOVE` -- remove specified patterns from the range
734
+ - `REPLACE` -- replace all existing patterns with the new set
735
+
736
+ #### Delete Range
737
+
738
+ ```python
739
+ VirtualExtensionsApi.delete_range(
740
+ extension_range_id: str,
741
+ org_id: str = None,
742
+ ) -> None
743
+ ```
744
+
745
+ #### Validate Range
746
+
747
+ ```python
748
+ VirtualExtensionsApi.validate_range(
749
+ location_id: str = None,
750
+ name: str = None,
751
+ prefix: str = None,
752
+ patterns: list[str] = None, # Max 100
753
+ range_id: str = None, # Include when validating existing range
754
+ org_id: str = None,
755
+ ) -> ValidateVirtualExtensionRange
756
+ ```
757
+
758
+ Pre-check before creating or modifying. Returns `status` of `OK` or `ERRORS`. When `ERRORS`, the `validation_status` list contains per-pattern details.
759
+
760
+ ### Extension Settings (Mode)
761
+
762
+ #### Get Mode
763
+
764
+ ```python
765
+ VirtualExtensionsApi.get_extension_settings(
766
+ org_id: str = None,
767
+ ) -> VirtualExtensionMode # STANDARD or ENHANCED
768
+ ```
769
+
770
+ #### Set Mode
771
+
772
+ ```python
773
+ VirtualExtensionsApi.modify_extension_settings(
774
+ mode: VirtualExtensionMode, # STANDARD or ENHANCED
775
+ org_id: str = None,
776
+ ) -> None
777
+ ```
778
+
779
+ **Standard mode** (default): Virtual extensions must have an E.164 prefix. No special PSTN provider support required.
780
+
781
+ **Enhanced mode**: Prefix can be E.164 or non-E.164, but requires PSTN provider support for special network signaling extensions. The API documentation states: "virtual extensions won't function properly in this mode unless your PSTN provider supports special network signaling extensions and there aren't many PSTN providers that do." No specific provider list is published in the API documentation or OpenAPI spec. Contact your Cisco account team or PSTN provider to confirm Enhanced mode support.
782
+
783
+ ### Data Models
784
+
785
+ #### VirtualExtension
786
+
787
+ ```python
788
+ class VirtualExtension(ApiModel):
789
+ id: Optional[str] # Unique identifier
790
+ extension: Optional[str] # Internal extension number
791
+ routing_prefix: Optional[str] # Location routing prefix
792
+ esn: Optional[str] # Enterprise Significant Number
793
+ phone_number: Optional[str] # External E.164 number
794
+ first_name: Optional[str]
795
+ last_name: Optional[str]
796
+ display_name: Optional[str]
797
+ level: Optional[VirtualExtensionLevel] # ORGANIZATION or LOCATION
798
+ location_id: Optional[str] # Set for location-level only
799
+ location_name: Optional[str] # Set for location-level only
800
+ ```
801
+
802
+ #### VirtualExtensionRange
803
+
804
+ ```python
805
+ class VirtualExtensionRange(ApiModel):
806
+ id: Optional[str] # Range ID
807
+ name: Optional[str] # Unique range name
808
+ prefix: Optional[str] # E.164 prefix (Standard) or any (Enhanced)
809
+ level: Optional[VirtualExtensionLevel] # ORGANIZATION or LOCATION
810
+ patterns: Optional[list[str]] # Extension patterns (max 100), "X" wildcards
811
+ location_id: Optional[str] # Set for location-level only
812
+ location_name: Optional[str] # Set for location-level only
813
+ ```
814
+
815
+ #### Enums
816
+
817
+ ```python
818
+ class VirtualExtensionLevel(str, Enum):
819
+ location = 'LOCATION'
820
+ organization = 'ORGANIZATION'
821
+
822
+ class VirtualExtensionMode(str, Enum):
823
+ standard = 'STANDARD'
824
+ enhanced = 'ENHANCED'
825
+
826
+ class VirtualExtensionRangeAction(str, Enum):
827
+ add = 'ADD'
828
+ remove = 'REMOVE'
829
+ replace = 'REPLACE'
830
+ ```
831
+
832
+ #### Validation Models
833
+
834
+ ```python
835
+ class ValidateVirtualExtensionRange(ApiModel):
836
+ status: Optional[ValidateVirtualExtensionStatus] # OK or ERRORS
837
+ validation_status: Optional[list[VirtualExtensionRangeValidationResult]]
838
+
839
+ class VirtualExtensionRangeValidationResult(ApiModel):
840
+ name: Optional[str]
841
+ prefix: Optional[str]
842
+ pattern: Optional[str]
843
+ error_code: Optional[int]
844
+ message: Optional[str]
845
+ status: Optional[VirtualExtensionValidationStatus] # VALID, DUPLICATE, DUPLICATE_IN_LIST, INVALID, LIMIT_EXCEEDED
846
+
847
+ class ValidatePhoneNumber(ApiModel):
848
+ status: Optional[ValidateVirtualExtensionStatus] # OK or ERRORS
849
+ phone_number_status: Optional[list[PhoneNumberStatus]]
850
+
851
+ class PhoneNumberStatus(ApiModel):
852
+ phone_number: Optional[str]
853
+ state: Optional[VirtualExtensionValidationStatus]
854
+ error_code: Optional[int]
855
+ message: Optional[str]
856
+ ```
857
+
858
+ ---
859
+
860
+ ## Raw HTTP
861
+ <!-- Updated by playbook session 2026-03-18 -->
862
+
863
+ All virtual line and virtual extension operations can be performed via raw HTTP using `api.session.rest_*()`. This is the preferred execution pattern -- wxc_sdk handles auth and session management, while you control the exact request.
864
+
865
+ ```python
866
+ from wxc_sdk import WebexSimpleApi
867
+ from wxc_sdk.rest import RestError
868
+
869
+ api = WebexSimpleApi()
870
+ BASE = "https://webexapis.com/v1"
871
+ ```
872
+
873
+ ### Virtual Lines CRUD
874
+
875
+ ```python
876
+ # ── List virtual lines ───────────────────────────────────────────
877
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualLines", params={
878
+ "max": 1000,
879
+ "locationId": location_id, # filter by location
880
+ })
881
+ virtual_lines = result.get("virtualLines", [])
882
+
883
+ # ── List with filters ────────────────────────────────────────────
884
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualLines", params={
885
+ "max": 1000,
886
+ "ownerName": "Sales",
887
+ "hasDeviceAssigned": "true",
888
+ "hasExtensionAssigned": "true",
889
+ })
890
+ virtual_lines = result.get("virtualLines", [])
891
+
892
+ # ── Get virtual line details ─────────────────────────────────────
893
+ vl = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{virtual_line_id}")
894
+
895
+ # ── Create a virtual line ────────────────────────────────────────
896
+ body = {
897
+ "firstName": "Sales",
898
+ "lastName": "Line",
899
+ "locationId": location_id,
900
+ "extension": "8500",
901
+ "phoneNumber": "+15551234567",
902
+ "callerIdLastName": "Sales",
903
+ "callerIdFirstName": "Department",
904
+ }
905
+ result = api.session.rest_post(f"{BASE}/telephony/config/virtualLines", json=body)
906
+ new_vl_id = result["id"]
907
+
908
+ # ── Update a virtual line ────────────────────────────────────────
909
+ body = {
910
+ "firstName": "Sales",
911
+ "lastName": "Main Line",
912
+ "extension": "8501",
913
+ }
914
+ api.session.rest_put(f"{BASE}/telephony/config/virtualLines/{virtual_line_id}", json=body)
915
+
916
+ # ── Delete a virtual line ────────────────────────────────────────
917
+ api.session.rest_delete(f"{BASE}/telephony/config/virtualLines/{virtual_line_id}")
918
+
919
+ # ── Get phone number ─────────────────────────────────────────────
920
+ num = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{virtual_line_id}/number")
921
+
922
+ # ── Update directory search ──────────────────────────────────────
923
+ api.session.rest_put(f"{BASE}/telephony/config/virtualLines/{virtual_line_id}/directorySearch", json={
924
+ "enabled": True,
925
+ })
926
+
927
+ # ── Get assigned devices ─────────────────────────────────────────
928
+ devices = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{virtual_line_id}/devices")
929
+
930
+ # ── Get DECT network handsets ────────────────────────────────────
931
+ dect = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{virtual_line_id}/dectNetworks")
932
+ ```
933
+
934
+ ### Virtual Line Call Settings
935
+
936
+ Virtual line call settings use the `/telephony/config/virtualLines/{id}/{feature}` path:
937
+
938
+ ```python
939
+ # ── Voicemail ────────────────────────────────────────────────────
940
+ vm = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/voicemail")
941
+ api.session.rest_put(f"{BASE}/telephony/config/virtualLines/{vl_id}/voicemail", json={
942
+ "enabled": True,
943
+ "sendAllCalls": {"enabled": False},
944
+ "sendBusyCalls": {"enabled": True, "greeting": "DEFAULT"},
945
+ "sendUnansweredCalls": {"enabled": True, "numberOfRings": 3, "greeting": "DEFAULT"},
946
+ })
947
+
948
+ # ── Call Forwarding ──────────────────────────────────────────────
949
+ fwd = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/callForwarding")
950
+ api.session.rest_put(f"{BASE}/telephony/config/virtualLines/{vl_id}/callForwarding", json={
951
+ "callForwarding": {"always": {"enabled": True, "destination": "+15551234567"}},
952
+ })
953
+
954
+ # ── Call Recording ───────────────────────────────────────────────
955
+ rec = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/callRecording")
956
+
957
+ # ── Call Waiting ─────────────────────────────────────────────────
958
+ cw = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/callWaiting")
959
+
960
+ # ── Caller ID ────────────────────────────────────────────────────
961
+ cid = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/callerId")
962
+
963
+ # ── DND ──────────────────────────────────────────────────────────
964
+ dnd = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/doNotDisturb")
965
+
966
+ # ── Call Intercept ───────────────────────────────────────────────
967
+ intercept = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/callIntercept")
968
+
969
+ # ── Privacy ──────────────────────────────────────────────────────
970
+ privacy = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/privacy")
971
+
972
+ # ── ECBN ─────────────────────────────────────────────────────────
973
+ ecbn = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/emergencyCallbackNumber")
974
+
975
+ # ── Incoming Permissions ─────────────────────────────────────────
976
+ perm_in = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/incomingPermission")
977
+
978
+ # ── Outgoing Permissions ─────────────────────────────────────────
979
+ perm_out = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/outgoingPermission")
980
+
981
+ # ── Music on Hold ────────────────────────────────────────────────
982
+ moh = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/musicOnHold")
983
+
984
+ # ── Barge-In ─────────────────────────────────────────────────────
985
+ barge = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/bargeIn")
986
+
987
+ # ── Push to Talk ─────────────────────────────────────────────────
988
+ ptt = api.session.rest_get(f"{BASE}/telephony/config/virtualLines/{vl_id}/pushToTalk")
989
+ ```
990
+
991
+ ### Virtual Extensions CRUD
992
+
993
+ ```python
994
+ # ── List virtual extensions ──────────────────────────────────────
995
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensions", params={
996
+ "max": 1000,
997
+ "locationId": location_id,
998
+ })
999
+ extensions = result.get("virtualExtensions", [])
1000
+
1001
+ # ── List org-level only ──────────────────────────────────────────
1002
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensions", params={
1003
+ "max": 1000,
1004
+ "orgLevelOnly": "true",
1005
+ })
1006
+
1007
+ # ── Get virtual extension details ────────────────────────────────
1008
+ ve = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensions/{extension_id}")
1009
+
1010
+ # ── Create a virtual extension ───────────────────────────────────
1011
+ body = {
1012
+ "displayName": "Alice Remote",
1013
+ "phoneNumber": "+15559876543",
1014
+ "extension": "7001",
1015
+ "firstName": "Alice",
1016
+ "lastName": "Remote",
1017
+ "locationId": location_id, # omit for org-level
1018
+ }
1019
+ result = api.session.rest_post(f"{BASE}/telephony/config/virtualExtensions", json=body)
1020
+ new_ve_id = result["id"]
1021
+
1022
+ # ── Update a virtual extension ───────────────────────────────────
1023
+ body = {
1024
+ "displayName": "Alice Remote (Updated)",
1025
+ "phoneNumber": "+15559876544",
1026
+ }
1027
+ api.session.rest_put(f"{BASE}/telephony/config/virtualExtensions/{extension_id}", json=body)
1028
+
1029
+ # ── Delete a virtual extension ───────────────────────────────────
1030
+ api.session.rest_delete(f"{BASE}/telephony/config/virtualExtensions/{extension_id}")
1031
+
1032
+ # ── Validate external phone numbers ─────────────────────────────
1033
+ result = api.session.rest_post(
1034
+ f"{BASE}/telephony/config/virtualExtensions/actions/validateNumbers/invoke",
1035
+ json={"phoneNumbers": ["+15551112222", "+15553334444"]},
1036
+ )
1037
+ # Returns: {status: "OK"|"ERRORS", phoneNumberStatus: [...]}
1038
+
1039
+ # ── Get/set extension mode ───────────────────────────────────────
1040
+ settings = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensions/settings")
1041
+ # Returns: {mode: "STANDARD"|"ENHANCED"}
1042
+
1043
+ api.session.rest_put(f"{BASE}/telephony/config/virtualExtensions/settings", json={
1044
+ "mode": "STANDARD",
1045
+ })
1046
+ ```
1047
+
1048
+ ### Virtual Extension Ranges
1049
+
1050
+ ```python
1051
+ # ── List ranges ──────────────────────────────────────────────────
1052
+ result = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensionRanges", params={
1053
+ "max": 1000,
1054
+ })
1055
+ ranges = result.get("virtualExtensionRanges", [])
1056
+
1057
+ # ── Get range details ────────────────────────────────────────────
1058
+ rng = api.session.rest_get(f"{BASE}/telephony/config/virtualExtensionRanges/{range_id}")
1059
+
1060
+ # ── Create a range ───────────────────────────────────────────────
1061
+ body = {
1062
+ "name": "Remote Office Block",
1063
+ "prefix": "+15559870000",
1064
+ "patterns": ["70XX", "71XX"],
1065
+ "locationId": location_id, # omit for org-level
1066
+ }
1067
+ result = api.session.rest_post(f"{BASE}/telephony/config/virtualExtensionRanges", json=body)
1068
+ new_range_id = result["id"]
1069
+
1070
+ # ── Modify a range (ADD/REMOVE/REPLACE patterns) ────────────────
1071
+ api.session.rest_put(f"{BASE}/telephony/config/virtualExtensionRanges/{range_id}", json={
1072
+ "name": "Remote Office Block",
1073
+ "prefix": "+15559870000",
1074
+ "patterns": ["72XX"],
1075
+ "action": "ADD", # ADD, REMOVE, or REPLACE
1076
+ })
1077
+
1078
+ # ── Delete a range ───────────────────────────────────────────────
1079
+ api.session.rest_delete(f"{BASE}/telephony/config/virtualExtensionRanges/{range_id}")
1080
+
1081
+ # ── Validate a range ─────────────────────────────────────────────
1082
+ result = api.session.rest_post(
1083
+ f"{BASE}/telephony/config/virtualExtensionRanges/actions/validate/invoke",
1084
+ json={
1085
+ "name": "Test Range",
1086
+ "prefix": "+15559870000",
1087
+ "patterns": ["70XX"],
1088
+ },
1089
+ )
1090
+ # Returns: {status: "OK"|"ERRORS", validationStatus: [...]}
1091
+ ```
1092
+
1093
+ ### Raw HTTP Gotchas
1094
+
1095
+ 1. **Virtual lines response key is `virtualLines`** -- Not `items`. This differs from the Workspaces API which uses `items`.
1096
+ 2. **Virtual extensions response key is `virtualExtensions`** -- Consistent with the domain-specific naming pattern.
1097
+ 3. **Virtual extension ranges response key is `virtualExtensionRanges`** -- Same pattern.
1098
+ 4. **Virtual line update is partial** -- Only include fields you want to change. Omitted fields are not modified. This differs from workspace update which is a full PUT.
1099
+ 5. **`action` is mandatory when modifying range patterns** -- If you include `patterns` in a range PUT, you must also include `action` (ADD, REMOVE, or REPLACE).
1100
+ 6. **`orgLevelOnly` is mutually exclusive with `locationId`/`locationName`** -- Only one filter type is allowed when listing virtual extensions or ranges.
1101
+ 7. **No auto-pagination** -- Use `max=1000` for the first page. Check for pagination links if you have more results.
1102
+ 8. **Virtual line call settings path vs workspace features path** -- Virtual lines use `/telephony/config/virtualLines/{id}/{feature}`. Workspaces use `/workspaces/{id}/features/{feature}`. These are completely different base paths.
1103
+ 9. **`virtual-extensions` CLI commands use wrong ID type.** The generated `virtual-extensions` command group maps to the Virtual Extensions API which uses `VIRTUAL_EXTENSION`-encoded IDs. Virtual lines created via `/telephony/config/virtualLines` use `VIRTUAL_LINE` IDs. `virtual-extensions list` returns empty, and `virtual-extensions delete` returns 400. **Workaround:** Use raw REST calls (`DELETE /v1/telephony/config/virtualLines/{id}`). The `wxcli cleanup` command already uses raw REST for this reason. The `virtual-line-settings` group uses the correct path family but only has settings commands, not CRUD. <!-- Documented from CLI known issue, 2026-03-31 -->
1104
+
1105
+ ---
1106
+
1107
+ ## Virtual Lines vs. Virtual Extensions: Decision Guide
1108
+
1109
+ | Question | Virtual Line | Virtual Extension |
1110
+ |----------|-------------|-------------------|
1111
+ | Does it need voicemail? | Yes | No |
1112
+ | Does it need call forwarding/recording? | Yes | No |
1113
+ | Does it ring a device inside Webex Calling? | Yes | No |
1114
+ | Does it route to an external PSTN number? | No (it IS a Webex line) | Yes |
1115
+ | Does it need its own call settings? | Yes (17+ sub-APIs) | No |
1116
+ | Can it be assigned to a physical phone? | Yes | No |
1117
+ | Does it need to integrate a remote PBX? | No | Yes |
1118
+
1119
+ ---
1120
+
1121
+ ## Source
1122
+
1123
+ - SDK source: `wxc_sdk/telephony/virtual_line/__init__.py`
1124
+ - SDK source: `wxc_sdk/telephony/virtual_extensions/__init__.py`
1125
+ - SDK source: `wxc_sdk/person_settings/common.py` (ApiSelector, URL routing)
1126
+
1127
+ ---
1128
+
1129
+ ## See Also
1130
+
1131
+ - **[devices-dect.md](devices-dect.md)** — DECT handset Line 2 supports VIRTUAL_LINE member type. Virtual lines can be assigned to DECT handsets as secondary lines.
1132
+ - **[emergency-services.md](emergency-services.md)** — Emergency callback number (ECBN) configuration for virtual lines. The `.ecbn` sub-API in the virtual line call settings table is documented in detail there.