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.
- wxcli/__init__.py +4 -0
- wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
- wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
- wxcli/_playbook/.claude/rules/cleanup.md +29 -0
- wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
- wxcli/_playbook/.claude/rules/org-health.md +14 -0
- wxcli/_playbook/.claude/settings.json +8 -0
- wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
- wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
- wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
- wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
- wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
- wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
- wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
- wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
- wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
- wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
- wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
- wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
- wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
- wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
- wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
- wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
- wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
- wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
- wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
- wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
- wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
- wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
- wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
- wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
- wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
- wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
- wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
- wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
- wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
- wxcli/_playbook/CLAUDE.md +373 -0
- wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
- wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
- wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
- wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
- wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
- wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
- wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
- wxcli/_playbook/docs/reference/admin-partner.md +357 -0
- wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
- wxcli/_playbook/docs/reference/authentication.md +965 -0
- wxcli/_playbook/docs/reference/call-control.md +1205 -0
- wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
- wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
- wxcli/_playbook/docs/reference/call-routing.md +2304 -0
- wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
- wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
- wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
- wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
- wxcli/_playbook/docs/reference/devices-core.md +1869 -0
- wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
- wxcli/_playbook/docs/reference/devices-platform.md +467 -0
- wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
- wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
- wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
- wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
- wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
- wxcli/_playbook/docs/reference/meetings-content.md +473 -0
- wxcli/_playbook/docs/reference/meetings-core.md +903 -0
- wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
- wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
- wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
- wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
- wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
- wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
- wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
- wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
- wxcli/_playbook/docs/reference/provisioning.md +1366 -0
- wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
- wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
- wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
- wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
- wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
- wxcli/_version.py +24 -0
- wxcli/auth.py +137 -0
- wxcli/commands/__init__.py +0 -0
- wxcli/commands/_registry.py +181 -0
- wxcli/commands/activation_email.py +103 -0
- wxcli/commands/admin_recordings.py +585 -0
- wxcli/commands/analytics.py +105 -0
- wxcli/commands/announcement_playlists.py +219 -0
- wxcli/commands/announcements.py +519 -0
- wxcli/commands/archive_users.py +36 -0
- wxcli/commands/attachment_actions.py +60 -0
- wxcli/commands/audit_events.py +83 -0
- wxcli/commands/authorizations.py +113 -0
- wxcli/commands/auto_attendant.py +642 -0
- wxcli/commands/broadworks_billing_reports.py +126 -0
- wxcli/commands/broadworks_enterprises.py +160 -0
- wxcli/commands/broadworks_subscribers.py +319 -0
- wxcli/commands/broadworks_workspaces.py +108 -0
- wxcli/commands/call_controls.py +1304 -0
- wxcli/commands/call_park.py +494 -0
- wxcli/commands/call_pickup.py +229 -0
- wxcli/commands/call_queue.py +1878 -0
- wxcli/commands/call_recording.py +707 -0
- wxcli/commands/call_routing.py +1610 -0
- wxcli/commands/call_settings_for_me_phase_5.py +212 -0
- wxcli/commands/caller_reputation.py +169 -0
- wxcli/commands/calling_service.py +305 -0
- wxcli/commands/cc_address_book.py +770 -0
- wxcli/commands/cc_agent_greetings.py +242 -0
- wxcli/commands/cc_agent_summaries.py +39 -0
- wxcli/commands/cc_agent_wellbeing.py +216 -0
- wxcli/commands/cc_agents.py +425 -0
- wxcli/commands/cc_ai_assistant.py +55 -0
- wxcli/commands/cc_ai_feature.py +294 -0
- wxcli/commands/cc_audio_files.py +149 -0
- wxcli/commands/cc_auto_csat.py +311 -0
- wxcli/commands/cc_aux_code.py +458 -0
- wxcli/commands/cc_business_hour.py +353 -0
- wxcli/commands/cc_call_monitoring.py +228 -0
- wxcli/commands/cc_callbacks.py +194 -0
- wxcli/commands/cc_campaign.py +212 -0
- wxcli/commands/cc_captures.py +39 -0
- wxcli/commands/cc_contact_list.py +169 -0
- wxcli/commands/cc_contact_number.py +295 -0
- wxcli/commands/cc_data_sources.py +235 -0
- wxcli/commands/cc_desktop_layout.py +380 -0
- wxcli/commands/cc_desktop_profile.py +322 -0
- wxcli/commands/cc_dial_number.py +511 -0
- wxcli/commands/cc_dial_plan.py +347 -0
- wxcli/commands/cc_dnc.py +101 -0
- wxcli/commands/cc_entry_point.py +545 -0
- wxcli/commands/cc_ewt.py +51 -0
- wxcli/commands/cc_flow.py +477 -0
- wxcli/commands/cc_global_vars.py +430 -0
- wxcli/commands/cc_holiday_list.py +335 -0
- wxcli/commands/cc_journey.py +1261 -0
- wxcli/commands/cc_legacy_flows.py +49 -0
- wxcli/commands/cc_multimedia_profile.py +436 -0
- wxcli/commands/cc_notification.py +51 -0
- wxcli/commands/cc_outdial_ani.py +604 -0
- wxcli/commands/cc_overrides.py +341 -0
- wxcli/commands/cc_queue.py +1083 -0
- wxcli/commands/cc_queue_stats.py +55 -0
- wxcli/commands/cc_realtime.py +51 -0
- wxcli/commands/cc_resource_collection.py +312 -0
- wxcli/commands/cc_search.py +46 -0
- wxcli/commands/cc_site.py +400 -0
- wxcli/commands/cc_skill.py +350 -0
- wxcli/commands/cc_skill_profile.py +289 -0
- wxcli/commands/cc_subscriptions.py +426 -0
- wxcli/commands/cc_summaries.py +130 -0
- wxcli/commands/cc_tasks.py +844 -0
- wxcli/commands/cc_team.py +325 -0
- wxcli/commands/cc_user_profiles.py +349 -0
- wxcli/commands/cc_users.py +701 -0
- wxcli/commands/cc_work_types.py +363 -0
- wxcli/commands/cdr.py +89 -0
- wxcli/commands/classifications.py +38 -0
- wxcli/commands/cleanup.py +1427 -0
- wxcli/commands/client_settings.py +72 -0
- wxcli/commands/conference.py +328 -0
- wxcli/commands/configure.py +72 -0
- wxcli/commands/converged_recordings.py +347 -0
- wxcli/commands/converged_recordings_export.py +310 -0
- wxcli/commands/cq_playlists.py +42 -0
- wxcli/commands/cucm.py +2794 -0
- wxcli/commands/cucm_config.py +129 -0
- wxcli/commands/customer_assist.py +373 -0
- wxcli/commands/data_sources.py +227 -0
- wxcli/commands/dect_devices.py +787 -0
- wxcli/commands/device_configurations.py +77 -0
- wxcli/commands/device_dynamic_settings.py +320 -0
- wxcli/commands/device_settings.py +1489 -0
- wxcli/commands/devices.py +280 -0
- wxcli/commands/domains.py +142 -0
- wxcli/commands/ecm.py +178 -0
- wxcli/commands/emergency_services.py +827 -0
- wxcli/commands/events.py +83 -0
- wxcli/commands/external_voicemail.py +52 -0
- wxcli/commands/groups.py +206 -0
- wxcli/commands/guest_management.py +76 -0
- wxcli/commands/hds.py +340 -0
- wxcli/commands/hot_desk.py +67 -0
- wxcli/commands/hot_desking_members.py +118 -0
- wxcli/commands/hot_desking_portal.py +124 -0
- wxcli/commands/hunt_group.py +577 -0
- wxcli/commands/hybrid_clusters.py +71 -0
- wxcli/commands/hybrid_connectors.py +67 -0
- wxcli/commands/identity_org.py +159 -0
- wxcli/commands/init_playbook.py +155 -0
- wxcli/commands/licenses.py +106 -0
- wxcli/commands/live_monitoring.py +40 -0
- wxcli/commands/location_call_handling.py +577 -0
- wxcli/commands/location_schedules.py +341 -0
- wxcli/commands/location_settings.py +1297 -0
- wxcli/commands/location_voicemail.py +494 -0
- wxcli/commands/locations.py +336 -0
- wxcli/commands/meeting_captions.py +110 -0
- wxcli/commands/meeting_chats.py +63 -0
- wxcli/commands/meeting_invitees.py +234 -0
- wxcli/commands/meeting_messages.py +28 -0
- wxcli/commands/meeting_participants.py +278 -0
- wxcli/commands/meeting_polls.py +111 -0
- wxcli/commands/meeting_preferences.py +515 -0
- wxcli/commands/meeting_qa.py +78 -0
- wxcli/commands/meeting_qualities.py +43 -0
- wxcli/commands/meeting_reports.py +101 -0
- wxcli/commands/meeting_session_types.py +105 -0
- wxcli/commands/meeting_site.py +57 -0
- wxcli/commands/meeting_slido.py +44 -0
- wxcli/commands/meeting_summaries.py +92 -0
- wxcli/commands/meeting_tracking_codes.py +233 -0
- wxcli/commands/meeting_transcripts.py +230 -0
- wxcli/commands/meetings.py +1997 -0
- wxcli/commands/memberships.py +164 -0
- wxcli/commands/messages.py +213 -0
- wxcli/commands/mode_management.py +244 -0
- wxcli/commands/my_call_settings.py +3577 -0
- wxcli/commands/numbers.py +405 -0
- wxcli/commands/operating_modes.py +410 -0
- wxcli/commands/org_contacts.py +284 -0
- wxcli/commands/org_health_cli.py +55 -0
- wxcli/commands/org_settings.py +71 -0
- wxcli/commands/organizations.py +83 -0
- wxcli/commands/paging_group.py +257 -0
- wxcli/commands/partner_admins.py +141 -0
- wxcli/commands/partner_reports.py +188 -0
- wxcli/commands/partner_tags.py +216 -0
- wxcli/commands/people.py +279 -0
- wxcli/commands/person_call_settings.py +71 -0
- wxcli/commands/pstn.py +280 -0
- wxcli/commands/recording_report.py +154 -0
- wxcli/commands/report_templates.py +38 -0
- wxcli/commands/reports.py +144 -0
- wxcli/commands/resource_group_memberships.py +160 -0
- wxcli/commands/resource_groups.py +67 -0
- wxcli/commands/roles.py +63 -0
- wxcli/commands/room_tabs.py +160 -0
- wxcli/commands/rooms.py +225 -0
- wxcli/commands/scim_bulk.py +46 -0
- wxcli/commands/scim_groups.py +246 -0
- wxcli/commands/scim_schemas.py +82 -0
- wxcli/commands/scim_users.py +283 -0
- wxcli/commands/security_audit.py +54 -0
- wxcli/commands/service_apps.py +51 -0
- wxcli/commands/single_number_reach.py +209 -0
- wxcli/commands/team_memberships.py +155 -0
- wxcli/commands/teams.py +153 -0
- wxcli/commands/ucm_profile.py +42 -0
- wxcli/commands/update.py +219 -0
- wxcli/commands/user_settings.py +5116 -0
- wxcli/commands/video_mesh.py +1090 -0
- wxcli/commands/virtual_extensions.py +502 -0
- wxcli/commands/virtual_line_settings.py +2072 -0
- wxcli/commands/webhooks.py +176 -0
- wxcli/commands/wholesale_billing_reports.py +147 -0
- wxcli/commands/wholesale_provisioning.py +523 -0
- wxcli/commands/workspace_locations.py +339 -0
- wxcli/commands/workspace_metrics.py +103 -0
- wxcli/commands/workspace_personalization.py +70 -0
- wxcli/commands/workspace_settings.py +3347 -0
- wxcli/commands/workspaces.py +279 -0
- wxcli/commands/xapi.py +114 -0
- wxcli/config.py +139 -0
- wxcli/errors.py +77 -0
- wxcli/main.py +210 -0
- wxcli/migration/CLAUDE.md +106 -0
- wxcli/migration/__init__.py +1 -0
- wxcli/migration/advisory/CLAUDE.md +196 -0
- wxcli/migration/advisory/__init__.py +46 -0
- wxcli/migration/advisory/advisor.py +107 -0
- wxcli/migration/advisory/advisory_patterns.py +2552 -0
- wxcli/migration/advisory/recommendation_rules.py +835 -0
- wxcli/migration/cucm/CLAUDE.md +74 -0
- wxcli/migration/cucm/__init__.py +5 -0
- wxcli/migration/cucm/connection.py +265 -0
- wxcli/migration/cucm/discovery.py +225 -0
- wxcli/migration/cucm/extractors/__init__.py +1 -0
- wxcli/migration/cucm/extractors/announcements.py +92 -0
- wxcli/migration/cucm/extractors/base.py +90 -0
- wxcli/migration/cucm/extractors/device_profiles.py +98 -0
- wxcli/migration/cucm/extractors/devices.py +193 -0
- wxcli/migration/cucm/extractors/e911.py +103 -0
- wxcli/migration/cucm/extractors/features.py +309 -0
- wxcli/migration/cucm/extractors/helpers.py +73 -0
- wxcli/migration/cucm/extractors/informational.py +295 -0
- wxcli/migration/cucm/extractors/locations.py +221 -0
- wxcli/migration/cucm/extractors/moh.py +100 -0
- wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
- wxcli/migration/cucm/extractors/routing.py +493 -0
- wxcli/migration/cucm/extractors/shared_lines.py +146 -0
- wxcli/migration/cucm/extractors/templates.py +229 -0
- wxcli/migration/cucm/extractors/tier4.py +173 -0
- wxcli/migration/cucm/extractors/users.py +240 -0
- wxcli/migration/cucm/extractors/voicemail.py +243 -0
- wxcli/migration/cucm/extractors/workspaces.py +88 -0
- wxcli/migration/cucm/unity_connection.py +304 -0
- wxcli/migration/execute/CLAUDE.md +372 -0
- wxcli/migration/execute/__init__.py +303 -0
- wxcli/migration/execute/batch.py +277 -0
- wxcli/migration/execute/dependency.py +483 -0
- wxcli/migration/execute/engine.py +964 -0
- wxcli/migration/execute/handlers.py +2232 -0
- wxcli/migration/execute/planner.py +1939 -0
- wxcli/migration/execute/runtime.py +571 -0
- wxcli/migration/export/CLAUDE.md +49 -0
- wxcli/migration/export/__init__.py +8 -0
- wxcli/migration/export/csv_export.py +126 -0
- wxcli/migration/export/deployment_plan.py +479 -0
- wxcli/migration/export/json_export.py +60 -0
- wxcli/migration/models.py +941 -0
- wxcli/migration/phone_models.py +50 -0
- wxcli/migration/preflight/CLAUDE.md +52 -0
- wxcli/migration/preflight/__init__.py +89 -0
- wxcli/migration/preflight/checks.py +911 -0
- wxcli/migration/preflight/runner.py +293 -0
- wxcli/migration/rate_limiter.py +144 -0
- wxcli/migration/report/CLAUDE.md +245 -0
- wxcli/migration/report/__init__.py +7 -0
- wxcli/migration/report/appendix.py +2437 -0
- wxcli/migration/report/assembler.py +183 -0
- wxcli/migration/report/charts.py +263 -0
- wxcli/migration/report/executive.py +776 -0
- wxcli/migration/report/explainer.py +704 -0
- wxcli/migration/report/helpers.py +74 -0
- wxcli/migration/report/ingest.py +152 -0
- wxcli/migration/report/notice_templates.py +192 -0
- wxcli/migration/report/score.py +375 -0
- wxcli/migration/report/styles.py +1052 -0
- wxcli/migration/report/user_diff.py +1052 -0
- wxcli/migration/report/user_notice.py +425 -0
- wxcli/migration/state.py +139 -0
- wxcli/migration/store.py +860 -0
- wxcli/migration/transform/CLAUDE.md +107 -0
- wxcli/migration/transform/__init__.py +12 -0
- wxcli/migration/transform/analysis_pipeline.py +453 -0
- wxcli/migration/transform/analyzers/__init__.py +142 -0
- wxcli/migration/transform/analyzers/css_permission.py +189 -0
- wxcli/migration/transform/analyzers/css_routing.py +335 -0
- wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
- wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
- wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
- wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
- wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
- wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
- wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
- wxcli/migration/transform/analyzers/missing_data.py +300 -0
- wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
- wxcli/migration/transform/analyzers/shared_line.py +170 -0
- wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
- wxcli/migration/transform/analyzers/workspace_license.py +212 -0
- wxcli/migration/transform/cross_reference.py +1136 -0
- wxcli/migration/transform/cucm_pattern.py +246 -0
- wxcli/migration/transform/decisions.py +301 -0
- wxcli/migration/transform/e164.py +145 -0
- wxcli/migration/transform/engine.py +256 -0
- wxcli/migration/transform/mappers/CLAUDE.md +123 -0
- wxcli/migration/transform/mappers/__init__.py +76 -0
- wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
- wxcli/migration/transform/mappers/base.py +190 -0
- wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
- wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
- wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
- wxcli/migration/transform/mappers/css_mapper.py +868 -0
- wxcli/migration/transform/mappers/dect_mapper.py +367 -0
- wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
- wxcli/migration/transform/mappers/device_mapper.py +227 -0
- wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
- wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
- wxcli/migration/transform/mappers/e911_mapper.py +129 -0
- wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
- wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
- wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
- wxcli/migration/transform/mappers/line_mapper.py +310 -0
- wxcli/migration/transform/mappers/location_mapper.py +235 -0
- wxcli/migration/transform/mappers/moh_mapper.py +114 -0
- wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
- wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
- wxcli/migration/transform/mappers/routing_mapper.py +821 -0
- wxcli/migration/transform/mappers/snr_mapper.py +201 -0
- wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
- wxcli/migration/transform/mappers/user_mapper.py +264 -0
- wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
- wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
- wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
- wxcli/migration/transform/normalizers.py +2008 -0
- wxcli/migration/transform/pattern_converter.py +106 -0
- wxcli/migration/transform/pipeline.py +271 -0
- wxcli/migration/transform/rules.py +255 -0
- wxcli/org_health/CLAUDE.md +63 -0
- wxcli/org_health/__init__.py +7 -0
- wxcli/org_health/analyze.py +93 -0
- wxcli/org_health/checks.py +458 -0
- wxcli/org_health/collector.py +62 -0
- wxcli/org_health/models.py +78 -0
- wxcli/org_health/report.py +300 -0
- wxcli/output.py +101 -0
- wxcli-1.2.0.dist-info/METADATA +505 -0
- wxcli-1.2.0.dist-info/RECORD +413 -0
- wxcli-1.2.0.dist-info/WHEEL +5 -0
- wxcli-1.2.0.dist-info/entry_points.txt +2 -0
- wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
- wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
- wxcli-1.2.0.dist-info/scm_version.json +8 -0
- 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.
|