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,835 @@
|
|
|
1
|
+
<!-- Not directly verified via CLI — documents wxcadm library (not generated CLI commands) -->
|
|
2
|
+
<!-- wxcadm v4.6.1 review 2026-03-27: All v4.5.0–v4.6.1 changes verified present. Meraki optional dependency (lazy import), CPAPI auto-init commented out, Applications(org) signature — all accurately documented. Location.set_default_moh/upload_moh_file removed from Location class (only in CPAPI section, which is correct). No discrepancies found. -->
|
|
3
|
+
# wxcadm Advanced Modules
|
|
4
|
+
|
|
5
|
+
Covers wxcadm capabilities that go **beyond** the standard Webex Calling API: RedSky E911 management, Meraki network-to-E911 integration, the CP-API (Control Hub internal API), application/service-app lifecycle, Wholesale provisioning, and the Bifrost internal API.
|
|
6
|
+
|
|
7
|
+
These modules are **unique to wxcadm** -- none of them have equivalents in `wxc_sdk`.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<!-- Updated by playbook session 2026-03-18 -->
|
|
12
|
+
## When to Use wxcadm vs Raw HTTP
|
|
13
|
+
|
|
14
|
+
**Everything in this doc is unique to wxcadm. There are no raw HTTP equivalents in the standard Webex API.**
|
|
15
|
+
|
|
16
|
+
| Use wxcadm when | Use raw HTTP when |
|
|
17
|
+
|---|---|
|
|
18
|
+
| RedSky E911 management (buildings, locations, network discovery wire-maps, HELD devices) | **Never** -- RedSky has its own API (`api.wxc.e911cloud.com`) with separate auth; wxcadm is the only Python wrapper |
|
|
19
|
+
| Meraki-to-RedSky automated wire-map sync | **Never** -- requires both Meraki Dashboard API and RedSky API orchestration that wxcadm handles |
|
|
20
|
+
| CP-API operations (VM PIN reset, MOH upload, AA greeting upload, workspace caller ID) | **Never** -- CP-API is Control Hub's internal API (`cpapi-a.wbx2.com`), not exposed through `api.webex.com` |
|
|
21
|
+
| Wholesale partner provisioning (customer/subscriber management) | **Never** -- the `/v1/wholesale/*` endpoints require partner-level tokens and are not part of the standard Webex Calling API |
|
|
22
|
+
| Service App lifecycle (create, authorize, token exchange) | **Never** -- involves multi-step OAuth flows that wxcadm orchestrates |
|
|
23
|
+
| Bifrost location enrichment | **Never** -- internal Webex API (`bifrost-a.wbx2.com`) not available publicly |
|
|
24
|
+
|
|
25
|
+
> **Note:** The playbook uses raw HTTP via `api.session.rest_*()` for standard CRUD operations against the public Webex API. None of the capabilities in this doc are accessible through the public API. If you need E911 management, CP-API operations, Wholesale provisioning, or Bifrost data, wxcadm is required.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Table of Contents
|
|
30
|
+
|
|
31
|
+
- [RedSky E911](#redsky-e911)
|
|
32
|
+
- [Architecture: Building / Location](#architecture-building--location)
|
|
33
|
+
- [Connecting to RedSky](#connecting-to-redsky)
|
|
34
|
+
- [Buildings](#buildings)
|
|
35
|
+
- [Locations](#locations)
|
|
36
|
+
- [Network Discovery (Wire-Mapping)](#network-discovery-wire-mapping)
|
|
37
|
+
- [HELD Devices](#held-devices)
|
|
38
|
+
- [Users](#users)
|
|
39
|
+
- [Meraki Integration](#meraki-integration)
|
|
40
|
+
- [Tag-Based Location Convention](#tag-based-location-convention)
|
|
41
|
+
- [Connecting to Meraki](#connecting-to-meraki)
|
|
42
|
+
- [Attaching RedSky to a Meraki Network](#attaching-redsky-to-a-meraki-network)
|
|
43
|
+
- [Running the Audit](#running-the-audit)
|
|
44
|
+
- [Audit Results](#audit-results)
|
|
45
|
+
- [Device Types](#device-types)
|
|
46
|
+
- [CP-API (Control Hub Internal API)](#cp-api-control-hub-internal-api)
|
|
47
|
+
- [Voicemail PIN Management](#voicemail-pin-management)
|
|
48
|
+
- [Workspace Caller ID](#workspace-caller-id)
|
|
49
|
+
- [Music on Hold Upload](#music-on-hold-upload)
|
|
50
|
+
- [Auto Attendant Greetings](#auto-attendant-greetings)
|
|
51
|
+
- [Numbers (Deprecated)](#numbers-deprecated)
|
|
52
|
+
- [Applications](#applications)
|
|
53
|
+
- [Listing Applications](#listing-applications)
|
|
54
|
+
- [Service Applications](#service-applications)
|
|
55
|
+
- [Token Management](#token-management)
|
|
56
|
+
- [Wholesale](#wholesale)
|
|
57
|
+
- [Customers and Locations](#customers-and-locations)
|
|
58
|
+
- [Subscriber Provisioning](#subscriber-provisioning)
|
|
59
|
+
- [Bifrost](#bifrost)
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## RedSky E911
|
|
64
|
+
|
|
65
|
+
**Module:** `wxcadm.redsky`
|
|
66
|
+
|
|
67
|
+
RedSky (now Intrado) powers Kari's Law / RAY BAUM's Act E911 compliance for Webex Calling in the US. wxcadm wraps the RedSky Horizon REST API (`api.wxc.e911cloud.com`) to manage buildings, locations, network discovery wire-maps, and device tracking -- all programmatically.
|
|
68
|
+
|
|
69
|
+
This is a **standalone** connection separate from the Webex API. RedSky has its own user accounts; you authenticate with a RedSky Horizon admin username and password, not a Webex token.
|
|
70
|
+
|
|
71
|
+
### Architecture: Building / Location
|
|
72
|
+
|
|
73
|
+
RedSky uses a two-tier hierarchy:
|
|
74
|
+
|
|
75
|
+
| Tier | wxcadm Class | Meaning |
|
|
76
|
+
|------|-------------|---------|
|
|
77
|
+
| **Building** | `RedSkyBuilding` | A physical building with a validated street address |
|
|
78
|
+
| **Location** | `RedSkyLocation` | A dispatchable zone within a building (floor, wing, zone) |
|
|
79
|
+
|
|
80
|
+
A Building contains one or more Locations. Network discovery entries (MAC, LLDP, BSSID, IP range) map to a **Location**, not a Building.
|
|
81
|
+
|
|
82
|
+
Webex Calling "Locations" are more abstract (could span a campus), so wxcadm deliberately does **not** auto-map between the two. The `add_building()` method bridges them when a 1:1 mapping is desired.
|
|
83
|
+
|
|
84
|
+
### Connecting to RedSky
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import wxcadm
|
|
88
|
+
|
|
89
|
+
redsky = wxcadm.RedSky(username="admin@domain.com", password="secret")
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
RedSky.__init__(self, username: str, password: str)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Authenticates against `https://api.wxc.e911cloud.com/auth-service/login`. Stores an access token internally and handles token refresh automatically when a 401 is encountered.
|
|
97
|
+
|
|
98
|
+
**Key attributes after init:**
|
|
99
|
+
|
|
100
|
+
| Attribute | Type | Description |
|
|
101
|
+
|-----------|------|-------------|
|
|
102
|
+
| `org_id` | `str` | The RedSky company/organization ID |
|
|
103
|
+
|
|
104
|
+
### Buildings
|
|
105
|
+
|
|
106
|
+
#### Listing buildings
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
@property
|
|
110
|
+
RedSky.buildings -> list[RedSkyBuilding]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Returns all buildings in the account. Handles pagination internally (100 per page).
|
|
114
|
+
|
|
115
|
+
#### Finding a building
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
RedSky.get_building_by_name(name: str) -> RedSkyBuilding | None
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Case-insensitive name match.
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
RedSky.get_building_by_webex_location(location: wxcadm.Location) -> RedSkyBuilding | None
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Tries to match by supplemental data (last 20 chars of the Webex Location ID), then falls back to case-insensitive name match. This works when the building was originally created via `add_building()`.
|
|
128
|
+
|
|
129
|
+
#### Creating a building from a Webex Location
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
RedSky.add_building(
|
|
133
|
+
webex_location: Optional[wxcadm.Location] = None,
|
|
134
|
+
address_string: Optional[str] = None,
|
|
135
|
+
create_location: bool = True
|
|
136
|
+
) -> RedSkyBuilding
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
- Accepts either a Webex `Location` instance or a raw address string
|
|
140
|
+
- When `create_location=True` (default), also creates a "Default" `RedSkyLocation` inside the building, with the location's main number as the ECBN (Emergency Callback Number)
|
|
141
|
+
- Raises `ValueError` for non-US locations
|
|
142
|
+
|
|
143
|
+
**Example: bulk-add all Webex locations as RedSky buildings**
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import wxcadm
|
|
147
|
+
|
|
148
|
+
webex = wxcadm.Webex(access_token, fast_mode=True)
|
|
149
|
+
redsky = wxcadm.RedSky(redsky_user, redsky_pass)
|
|
150
|
+
|
|
151
|
+
for location in webex.org.locations:
|
|
152
|
+
if location.address['country'] != "US":
|
|
153
|
+
continue
|
|
154
|
+
building = redsky.add_building(location, create_location=True)
|
|
155
|
+
print(f"{building.name} -> {building.id}")
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
#### RedSkyBuilding attributes and methods
|
|
159
|
+
|
|
160
|
+
| Attribute / Method | Type / Signature | Description |
|
|
161
|
+
|--------------------|-----------------|-------------|
|
|
162
|
+
| `id` | `str` | Building ID |
|
|
163
|
+
| `name` | `str` | Building name |
|
|
164
|
+
| `address` | `dict` | Physical address (includes `normalizedAddress`, `streetAddress`) |
|
|
165
|
+
| `supplemental_data` | `str` | Custom data (wxcadm stores last 20 chars of Webex Location ID here) |
|
|
166
|
+
| `type` | `str` | Building type |
|
|
167
|
+
| `locations` | `list[RedSkyLocation]` | All locations within the building |
|
|
168
|
+
| `get_location_by_name(name: str)` | `-> RedSkyLocation \| None` | Case-insensitive location lookup |
|
|
169
|
+
| `add_location(location_name: str, ecbn: str = "", location_info: str = "")` | `-> bool` | Creates a new location in the building |
|
|
170
|
+
| `bssid_discovery` | `list[dict]` | All BSSID entries across all locations in this building |
|
|
171
|
+
| `lldp_discovery` | `list[dict]` | All LLDP entries across all locations in this building |
|
|
172
|
+
| `mac_discovery` | `list[dict]` | All MAC entries across all locations in this building |
|
|
173
|
+
| `ip_range_discovery` | `list[dict]` | All IP range entries across all locations in this building |
|
|
174
|
+
|
|
175
|
+
### Locations
|
|
176
|
+
|
|
177
|
+
`RedSkyLocation` represents a dispatchable zone inside a building.
|
|
178
|
+
|
|
179
|
+
| Attribute | Type | Description |
|
|
180
|
+
|-----------|------|-------------|
|
|
181
|
+
| `id` | `str` | Location ID (used in all network discovery mappings) |
|
|
182
|
+
| `name` | `str` | Location name (e.g., "Floor 1", "Default") |
|
|
183
|
+
| `address` | `dict` | Address information |
|
|
184
|
+
| `elin` | `dict` | Emergency Location Identification Number |
|
|
185
|
+
| `info` | `str` | Additional location info text |
|
|
186
|
+
| `address_entity_name` | `str` | Address entity name from RedSky |
|
|
187
|
+
| `bssid_discovery` | `list[dict]` | BSSID entries mapped to this location |
|
|
188
|
+
| `lldp_discovery` | `list[dict]` | LLDP entries mapped to this location |
|
|
189
|
+
| `mac_discovery` | `list[dict]` | MAC entries mapped to this location |
|
|
190
|
+
| `ip_range_discovery` | `list[dict]` | IP range entries mapped to this location |
|
|
191
|
+
|
|
192
|
+
### Network Discovery (Wire-Mapping)
|
|
193
|
+
|
|
194
|
+
RedSky determines a caller's dispatchable address by matching the device's network fingerprint against stored mappings. wxcadm supports all four discovery types.
|
|
195
|
+
|
|
196
|
+
#### MAC Address Discovery
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
RedSky.get_mac_discovery(mac: Optional[str] = None) -> list | dict | None
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Without args: returns all MAC mappings. With `mac`: returns the matching entry or `None`.
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
RedSky.add_mac_discovery(
|
|
206
|
+
mac: str,
|
|
207
|
+
location: RedSkyLocation,
|
|
208
|
+
description: str = ""
|
|
209
|
+
) -> dict
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
RedSky.delete_mac_discovery(
|
|
214
|
+
mac: Optional[str] = None,
|
|
215
|
+
entry_id: Optional[str] = None
|
|
216
|
+
) -> bool
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Pass either `mac` (looks up the entry for you) or `entry_id` (the `['id']` from a previous `get_mac_discovery()` call). If both are given, `entry_id` takes precedence.
|
|
220
|
+
|
|
221
|
+
#### LLDP Chassis/Port Discovery
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
RedSky.get_lldp_discovery() -> list[dict]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Returns all chassis mappings, each with a nested `ports` list.
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
RedSky.get_lldp_discovery_by_chassis(chassis_id: str) -> dict | None
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
RedSky.add_lldp_discovery(
|
|
235
|
+
chassis: str,
|
|
236
|
+
location: RedSkyLocation,
|
|
237
|
+
ports: list = None, # e.g. ['1', '2', '3'] or ['B000B4BB1BF4:P1']
|
|
238
|
+
description: str = ""
|
|
239
|
+
) -> dict
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
If `ports` are provided and the chassis already exists, only the ports are added. If the chassis doesn't exist, it's created first.
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
RedSky.delete_lldp_chassis(chassis_id: str, delete_ports: bool = False) -> bool
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
When `delete_ports=True`, all port mappings are deleted before the chassis.
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
RedSky.update_lldp_location(
|
|
252
|
+
entry_id: str,
|
|
253
|
+
chassis: str,
|
|
254
|
+
new_location: RedSkyLocation,
|
|
255
|
+
description: str
|
|
256
|
+
) -> bool
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
RedSky.update_lldp_port_location(
|
|
261
|
+
entry_id: str,
|
|
262
|
+
chassis_id: str,
|
|
263
|
+
port: str,
|
|
264
|
+
new_location: RedSkyLocation,
|
|
265
|
+
description: str
|
|
266
|
+
) -> bool
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### BSSID (WiFi Access Point) Discovery
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
RedSky.get_bssid_discovery(bssid: Optional[str] = None) -> list | dict | None
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
RedSky.add_bssid_discovery(
|
|
277
|
+
bssid: str,
|
|
278
|
+
location: RedSkyLocation,
|
|
279
|
+
description: str = "",
|
|
280
|
+
masking: bool = True # apply default last-digit masking
|
|
281
|
+
) -> dict
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
RedSky.delete_bssid_discovery(
|
|
286
|
+
bssid: Optional[str] = None,
|
|
287
|
+
entry_id: Optional[str] = None
|
|
288
|
+
) -> bool
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### IP Range Discovery
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
RedSky.get_ip_range_discovery(
|
|
295
|
+
ip_start: Optional[str] = None,
|
|
296
|
+
ip_end: Optional[str] = None,
|
|
297
|
+
range_for_ip: Optional[str] = None, # find the range containing this IP
|
|
298
|
+
type: Optional[str] = 'private' # 'private' or 'public'
|
|
299
|
+
) -> list | dict | None
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
`range_for_ip` is particularly useful: pass an IP address and it returns the range entry that contains it.
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
RedSky.add_ip_range_discovery(
|
|
306
|
+
ip_start: str,
|
|
307
|
+
ip_end: str,
|
|
308
|
+
location: RedSkyLocation,
|
|
309
|
+
description: str = ""
|
|
310
|
+
) -> dict
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
```python
|
|
314
|
+
RedSky.add_public_ip_range(
|
|
315
|
+
ip_start: str,
|
|
316
|
+
ip_end: str,
|
|
317
|
+
description: str = ""
|
|
318
|
+
) -> dict
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Public IP ranges are **not** mapped to a Location. They identify recognized corporate networks so RedSky accepts HELD requests from them.
|
|
322
|
+
|
|
323
|
+
```python
|
|
324
|
+
RedSky.delete_ip_range_discovery(ip_start: str, ip_end: str) -> bool
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### HELD Devices
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
@property
|
|
331
|
+
RedSky.held_devices -> list[dict]
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Returns all HELD and HELD+ devices known to RedSky.
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
RedSky.phones_without_location() -> list[dict]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Devices of type `HELD` (desk phones) that have no associated location.
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
RedSky.clients_without_location() -> list[dict]
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Devices of type `HELD_PLUS` (soft clients / Webex app) that have no associated location.
|
|
347
|
+
|
|
348
|
+
### Users
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
@property
|
|
352
|
+
RedSky.users -> RedSkyUsers # extends UserList
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**RedSkyUsers** is a `UserList` of `RedSkyUser` dataclass instances.
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
RedSkyUsers.get_by_email(email: str) -> RedSkyUser | None
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**RedSkyUser** attributes and properties:
|
|
362
|
+
|
|
363
|
+
| Attribute / Property | Type | Description |
|
|
364
|
+
|---------------------|------|-------------|
|
|
365
|
+
| `id` | `str` | RedSky user ID |
|
|
366
|
+
| `email` | `str` | User's email (mapped from `heldUserId` in RedSky) |
|
|
367
|
+
| `user_locations` | `list[dict]` | Locations the user has manually entered (via Webex app or MyE911) |
|
|
368
|
+
| `devices` | `list[dict]` | Soft client devices associated with the user (not desk phones) |
|
|
369
|
+
|
|
370
|
+
```python
|
|
371
|
+
RedSky.get_all_locations() -> dict[str, list[dict]]
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Returns `{'corporate': [...], 'personal': [...]}` -- corporate locations from the org and personal locations entered by individual users.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Meraki Integration
|
|
379
|
+
|
|
380
|
+
**Module:** `wxcadm.meraki`
|
|
381
|
+
|
|
382
|
+
Provides automated synchronization between Meraki Dashboard device inventory and RedSky Network Discovery. This is the "killer feature" for E911 wire-mapping: network engineers tag devices in Meraki, and wxcadm pushes the correct LLDP/BSSID mappings to RedSky.
|
|
383
|
+
|
|
384
|
+
**Requires the `meraki` Python library:** install with `pip install wxcadm[meraki]` or `pip install meraki`.
|
|
385
|
+
|
|
386
|
+
### Tag-Based Location Convention
|
|
387
|
+
|
|
388
|
+
Meraki devices and switch ports are tagged with a `911-` prefix to indicate the RedSky Location name:
|
|
389
|
+
|
|
390
|
+
| Tag in Meraki | RedSky Location Name |
|
|
391
|
+
|---------------|---------------------|
|
|
392
|
+
| `911-Floor_1` | `Floor 1` |
|
|
393
|
+
| `911-West_Wing` | `West Wing` |
|
|
394
|
+
| `911-Server_Room` | `Server Room` |
|
|
395
|
+
|
|
396
|
+
Underscores in the tag are converted to spaces. Devices without a `911-` tag are ignored during audits.
|
|
397
|
+
|
|
398
|
+
**Building determination:** the Meraki device's address field is matched against RedSky building addresses. Matching is attempted first on the full normalized address, then falls back to street address only.
|
|
399
|
+
|
|
400
|
+
### Connecting to Meraki
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
import wxcadm
|
|
404
|
+
|
|
405
|
+
meraki = wxcadm.Meraki(api_key="your_meraki_dashboard_api_key")
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
```python
|
|
409
|
+
Meraki.__init__(self, api_key: Optional[str] = None)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Initializes a connection to the Meraki Dashboard API.
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
Meraki.get_orgs() -> list[MerakiOrg]
|
|
416
|
+
Meraki.get_org_by_name(name: str) -> MerakiOrg | None
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
```python
|
|
420
|
+
MerakiOrg.get_networks() -> list[MerakiNetwork]
|
|
421
|
+
MerakiOrg.get_network_by_name(name: str) -> MerakiNetwork | None
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### Attaching RedSky to a Meraki Network
|
|
425
|
+
|
|
426
|
+
Before running an audit, a RedSky connection must be attached to the network:
|
|
427
|
+
|
|
428
|
+
```python
|
|
429
|
+
MerakiNetwork.attach_redsky(username: str, password: str) -> bool
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
This creates a `RedSky` instance and propagates it to all devices in the network.
|
|
433
|
+
|
|
434
|
+
### Running the Audit
|
|
435
|
+
|
|
436
|
+
```python
|
|
437
|
+
MerakiNetwork.redsky_audit(simulate: bool = False) -> MerakiAuditResults
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
The audit iterates over all switches and access points in the network:
|
|
441
|
+
|
|
442
|
+
**For switches (model contains "MS"):**
|
|
443
|
+
1. Matches the Meraki device address to a RedSky Building
|
|
444
|
+
2. Reads the `911-` tag on the switch to determine the chassis Location
|
|
445
|
+
3. Creates the Location in RedSky if it doesn't exist
|
|
446
|
+
4. Creates or updates the LLDP chassis mapping
|
|
447
|
+
5. For each tagged port, creates or updates the LLDP port mapping (port-level tags override the chassis Location)
|
|
448
|
+
|
|
449
|
+
**For access points (model contains "MR"):**
|
|
450
|
+
1. Matches the Meraki device address to a RedSky Building
|
|
451
|
+
2. Reads the `911-` tag to determine the AP Location
|
|
452
|
+
3. Creates the Location if it doesn't exist
|
|
453
|
+
4. Retrieves all BSSIDs from the AP's wireless status
|
|
454
|
+
5. Creates BSSID mappings for any enabled BSSIDs not already in RedSky
|
|
455
|
+
|
|
456
|
+
When `simulate=True`, no changes are made to RedSky. The `MerakiAuditResults` still reports what *would* have changed.
|
|
457
|
+
|
|
458
|
+
**Complete example:**
|
|
459
|
+
|
|
460
|
+
```python
|
|
461
|
+
import wxcadm
|
|
462
|
+
|
|
463
|
+
meraki = wxcadm.Meraki("meraki_api_key")
|
|
464
|
+
org = meraki.get_org_by_name("My Org")
|
|
465
|
+
network = org.get_network_by_name("Main Office")
|
|
466
|
+
network.attach_redsky(username="redsky_admin", password="redsky_pass")
|
|
467
|
+
|
|
468
|
+
# Dry run first
|
|
469
|
+
results = network.redsky_audit(simulate=True)
|
|
470
|
+
print(f"Would add {len(results.added_chassis)} chassis entries")
|
|
471
|
+
print(f"Would add {len(results.added_bssid)} BSSID entries")
|
|
472
|
+
print(f"Missing buildings: {results.missing_buildings}")
|
|
473
|
+
|
|
474
|
+
# Then run for real
|
|
475
|
+
results = network.redsky_audit()
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Audit Results
|
|
479
|
+
|
|
480
|
+
`MerakiAuditResults` has the following attributes (all are lists):
|
|
481
|
+
|
|
482
|
+
| Attribute | Contents |
|
|
483
|
+
|-----------|----------|
|
|
484
|
+
| `missing_buildings` | Addresses with no matching RedSky building |
|
|
485
|
+
| `added_locations` | `[{'building': RedSkyBuilding, 'location': str}]` |
|
|
486
|
+
| `switches` | All MerakiSwitch devices found |
|
|
487
|
+
| `access_points` | All MerakiWireless devices found |
|
|
488
|
+
| `added_chassis` | Chassis LLDP entries added |
|
|
489
|
+
| `added_ports` | Port LLDP entries added |
|
|
490
|
+
| `ignored_devices` | Devices skipped (no `911-` tag) |
|
|
491
|
+
| `updated_locations` | Existing entries whose Location was corrected |
|
|
492
|
+
| `added_bssid` | BSSID strings added to RedSky |
|
|
493
|
+
|
|
494
|
+
### Device Types
|
|
495
|
+
|
|
496
|
+
| Class | Base | Represents |
|
|
497
|
+
|-------|------|-----------|
|
|
498
|
+
| `MerakiDevice` | -- | Base class; holds `address`, `serial`, `mac`, `model`, `tags`, `name` |
|
|
499
|
+
| `MerakiSwitch` | `MerakiDevice` | Switch (model contains "MS"); has `get_ports() -> list[MerakiSwitchPort]` |
|
|
500
|
+
| `MerakiWireless` | `MerakiDevice` | Access point (model contains "MR"); has `get_bss_list() -> list[dict]` |
|
|
501
|
+
| `MerakiSwitchPort` | -- | Individual port; holds `switch`, `port_id`, `name`, `tags`, `enabled`, `type` |
|
|
502
|
+
|
|
503
|
+
All devices have `get_redsky_building(redsky: Optional[RedSky] = None) -> RedSkyBuilding | None` for manual building lookups outside of the audit workflow.
|
|
504
|
+
|
|
505
|
+
**Utility functions:**
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
wxcadm.meraki.tags_decoder(tags: list, match_string: Optional[str] = "911-") -> Optional[str]
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Finds the first tag matching `match_string`, strips the prefix, converts underscores to spaces.
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## CP-API (Control Hub Internal API)
|
|
516
|
+
|
|
517
|
+
**Module:** `wxcadm.cpapi`
|
|
518
|
+
|
|
519
|
+
The CP-API is the **internal** API used by the Webex Control Hub web UI (`cpapi-a.wbx2.com`). wxcadm wraps it to expose operations that are **not available** through the public Webex developer API.
|
|
520
|
+
|
|
521
|
+
> **Warning:** All CP-API methods require an access token with CP-API scope. A standard developer token may not have this. If the scope is missing, methods raise `TokenError`.
|
|
522
|
+
|
|
523
|
+
```python
|
|
524
|
+
CPAPI.__init__(self, org: wxcadm.Org, access_token: str)
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
Normally instantiated automatically by Org methods, not manually.
|
|
528
|
+
|
|
529
|
+
### Voicemail PIN Management
|
|
530
|
+
|
|
531
|
+
```python
|
|
532
|
+
CPAPI.set_global_vm_pin(pin: str) -> bool
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
Sets the org-wide default voicemail PIN. Raises `ValueError` if the PIN doesn't comply with the security policy.
|
|
536
|
+
|
|
537
|
+
```python
|
|
538
|
+
CPAPI.clear_global_vm_pin() -> str
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
Disables the org-wide default PIN.
|
|
542
|
+
|
|
543
|
+
```python
|
|
544
|
+
CPAPI.reset_vm_pin(person: Person, pin: str = None) -> bool
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
Resets a user's VM PIN to the org default. If `pin` is provided, temporarily sets the global PIN to that value, resets the user, then clears it -- enabling per-user PIN assignment.
|
|
548
|
+
|
|
549
|
+
The `reset_vm_pin` method's temporary global-pin-set-and-clear pattern will cause race conditions if called concurrently for multiple users. The source confirms there is no locking: it calls `set_global_vm_pin(pin)`, resets the user, then `clear_global_vm_pin()` sequentially with no mutex.
|
|
550
|
+
|
|
551
|
+
### Workspace Caller ID
|
|
552
|
+
|
|
553
|
+
```python
|
|
554
|
+
CPAPI.change_workspace_caller_id(
|
|
555
|
+
workspace_id: str, # CP-API workspace (Place) ID, not the Webex Calling ID
|
|
556
|
+
name: str, # externalCallerIdNamePolicy value
|
|
557
|
+
number: str # selected number value
|
|
558
|
+
) -> bool
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Changes the external caller ID for a workspace. Note that the CP-API uses a different workspace ID than the Webex Calling API.
|
|
562
|
+
|
|
563
|
+
### Music on Hold Upload
|
|
564
|
+
|
|
565
|
+
```python
|
|
566
|
+
CPAPI.upload_moh_file(location_id: str, filename: str) -> bool
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Uploads a WAV file as the custom Music on Hold for a location. `location_id` is the Base64-encoded Webex Location ID.
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
CPAPI.set_custom_moh(location_id: str, filename: str) -> bool
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Activates the uploaded custom MOH file for the location.
|
|
576
|
+
|
|
577
|
+
```python
|
|
578
|
+
CPAPI.set_default_moh(location_id: str) -> bool
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
Reverts to the system default MOH.
|
|
582
|
+
|
|
583
|
+
### Auto Attendant Greetings
|
|
584
|
+
|
|
585
|
+
```python
|
|
586
|
+
CPAPI.upload_aa_greeting(autoattendant, type: str, filename: str) -> bool
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Uploads a WAV greeting file. `type` must be `"business_hours"` or `"after_hours"`.
|
|
590
|
+
|
|
591
|
+
```python
|
|
592
|
+
CPAPI.set_custom_aa_greeting(autoattendant, type: str, filename: str) -> bool
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Activates the uploaded custom greeting and sets the greeting mode to `CUSTOM`.
|
|
596
|
+
|
|
597
|
+
### Numbers (Deprecated)
|
|
598
|
+
|
|
599
|
+
```python
|
|
600
|
+
CPAPI.get_numbers() -> list[dict]
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
This method is marked as deprecated in the source with the comment: "This method is deprecated and will likely be removed eventually. The Webex API now supports a Numbers GET." The CP-API wrapper is unnecessary for orgs using the public API.
|
|
604
|
+
|
|
605
|
+
### Workspace Calling Location
|
|
606
|
+
|
|
607
|
+
```python
|
|
608
|
+
CPAPI.get_workspace_calling_location(workspace_id: str) -> Location
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
Returns the Webex Calling `Location` instance for a workspace. Useful because Webex Calling uses a different Location model than the standard Workspace Location.
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
## Applications
|
|
616
|
+
|
|
617
|
+
**Module:** `wxcadm.applications`
|
|
618
|
+
|
|
619
|
+
Manages Webex applications (integrations, bots, service apps) via the public `/v1/applications` API.
|
|
620
|
+
|
|
621
|
+
### Listing Applications
|
|
622
|
+
|
|
623
|
+
```python
|
|
624
|
+
WebexApplications(org: wxcadm.Org) # auto-initialized; extends UserList
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
Accessed as `org.applications`.
|
|
628
|
+
|
|
629
|
+
```python
|
|
630
|
+
WebexApplications.org_apps -> list[WebexApplication]
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
Filters to only apps owned by the current org.
|
|
634
|
+
|
|
635
|
+
```python
|
|
636
|
+
WebexApplications.get_app_by_name(name: str) -> WebexApplication | list | None
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
Returns a single instance if one match, a list if multiple matches (same-name apps are allowed), or `None`.
|
|
640
|
+
|
|
641
|
+
```python
|
|
642
|
+
WebexApplications.get_app_by_id(id: str) -> WebexApplication
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
Checks local cache first, then calls the API if not found.
|
|
646
|
+
|
|
647
|
+
### Service Applications
|
|
648
|
+
|
|
649
|
+
```python
|
|
650
|
+
WebexApplications.add_service_application(
|
|
651
|
+
name: str,
|
|
652
|
+
contact_email: str,
|
|
653
|
+
scopes: list,
|
|
654
|
+
logo: str = "https://pngimg.com/uploads/hacker/hacker_PNG6.png"
|
|
655
|
+
) -> dict
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
Creates a Service App (formerly Authorized App). The response includes `clientSecret` -- **store it immediately**, as it will not be visible later.
|
|
659
|
+
|
|
660
|
+
The wxcadm source docstring says: "The Service App capability is in Early Field Trial and may not be available for your Org." Whether this has since reached GA would require checking the live Webex developer portal.
|
|
661
|
+
|
|
662
|
+
**Service App authorization flow:**
|
|
663
|
+
|
|
664
|
+
1. **Developer** creates the app with `add_service_application()`
|
|
665
|
+
2. **Admin** of the target org retrieves the app by ID and calls `app.authorize()`
|
|
666
|
+
3. **Developer** calls `app.get_token(client_secret, target_org_id)` to get an access token
|
|
667
|
+
4. Token refresh via `app.get_token_refresh(client_secret, refresh_token)`
|
|
668
|
+
|
|
669
|
+
### Token Management
|
|
670
|
+
|
|
671
|
+
**WebexApplication** is a `@dataclass` with the following key methods:
|
|
672
|
+
|
|
673
|
+
```python
|
|
674
|
+
WebexApplication.authorize() -> bool
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Authorizes the app for the org.
|
|
678
|
+
|
|
679
|
+
```python
|
|
680
|
+
WebexApplication.get_token(
|
|
681
|
+
client_secret: str,
|
|
682
|
+
target_org: wxcadm.Org | str # Org instance or Base64 org ID
|
|
683
|
+
) -> dict
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
Returns `{'access_token': ..., 'expires_in': ..., 'refresh_token': ..., 'refresh_token_expires_in': ..., 'token_type': 'Bearer'}`.
|
|
687
|
+
|
|
688
|
+
```python
|
|
689
|
+
WebexApplication.get_token_refresh(client_secret: str, refresh_token: str) -> dict
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
```python
|
|
693
|
+
WebexApplication.regenerate_client_secret() -> str | False
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
Generates a new client secret if the original was lost or compromised.
|
|
697
|
+
|
|
698
|
+
```python
|
|
699
|
+
WebexApplication.delete()
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Deletes the application entirely.
|
|
703
|
+
|
|
704
|
+
**Notable WebexApplication attributes (dataclass fields):**
|
|
705
|
+
|
|
706
|
+
`id`, `name`, `type`, `friendlyId`, `orgId`, `clientId`, `scopes`, `redirectUrls`, `botEmail`, `botPersonId`, `description`, `contactEmail`, `companyName`, `createdBy`, `created`, `modified`, `submissionStatus`
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## Wholesale
|
|
711
|
+
|
|
712
|
+
**Module:** `wxcadm.wholesale`
|
|
713
|
+
|
|
714
|
+
For **service providers** using the Webex Wholesale program. Provides customer and subscriber management through the `/v1/wholesale/*` API endpoints.
|
|
715
|
+
|
|
716
|
+
```python
|
|
717
|
+
Wholesale.__init__(self, access_token: str)
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
Standalone entry point (not under `Webex` or `Org`). Uses the partner's access token.
|
|
721
|
+
|
|
722
|
+
### Customers and Locations
|
|
723
|
+
|
|
724
|
+
```python
|
|
725
|
+
@property
|
|
726
|
+
Wholesale.customers -> list[WholesaleCustomer]
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
```python
|
|
730
|
+
Wholesale.get_customer(
|
|
731
|
+
id: str = None,
|
|
732
|
+
name: str = None,
|
|
733
|
+
spark_id: str = None
|
|
734
|
+
) -> WholesaleCustomer | None
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
Searches by ID, external name, or decoded Spark ID.
|
|
738
|
+
|
|
739
|
+
```python
|
|
740
|
+
@property
|
|
741
|
+
Wholesale.orgs -> list[Org]
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
Returns wxcadm `Org` instances for each wholesale customer, enabling the full wxcadm Org feature set on customer orgs.
|
|
745
|
+
|
|
746
|
+
**WholesaleCustomer** attributes:
|
|
747
|
+
|
|
748
|
+
| Attribute | Type | Description |
|
|
749
|
+
|-----------|------|-------------|
|
|
750
|
+
| `id` | `str` | Customer ID |
|
|
751
|
+
| `org_id` | `str` | Webex Org ID |
|
|
752
|
+
| `external_id` | `str` | Partner-assigned external identifier (used as "name") |
|
|
753
|
+
| `address` | `dict` | Customer address |
|
|
754
|
+
| `status` | `str` | Customer status |
|
|
755
|
+
| `packages` | `list` | Assigned packages |
|
|
756
|
+
| `resource_details` | `dict` | Resource detail information |
|
|
757
|
+
| `spark_id` | `str` (property) | Decoded Spark ID |
|
|
758
|
+
| `locations` | `list[Location]` (property) | Customer's locations as wxcadm `Location` instances |
|
|
759
|
+
|
|
760
|
+
```python
|
|
761
|
+
WholesaleCustomer.get_location(
|
|
762
|
+
id: str = None,
|
|
763
|
+
name: str = None,
|
|
764
|
+
spark_id: str = None
|
|
765
|
+
) -> Location | None
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### Subscriber Provisioning
|
|
769
|
+
|
|
770
|
+
```python
|
|
771
|
+
WholesaleCustomer.add_subscriber(
|
|
772
|
+
email: str,
|
|
773
|
+
package: str,
|
|
774
|
+
first_name: str,
|
|
775
|
+
last_name: str,
|
|
776
|
+
phone_number: str,
|
|
777
|
+
extension: str,
|
|
778
|
+
location: Location
|
|
779
|
+
) -> dict
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
Provisions a new subscriber (end user) under a wholesale customer with the specified calling package and location assignment.
|
|
783
|
+
|
|
784
|
+
---
|
|
785
|
+
|
|
786
|
+
## Bifrost
|
|
787
|
+
|
|
788
|
+
**Module:** `wxcadm.bifrost`
|
|
789
|
+
|
|
790
|
+
Bifrost is a **Webex internal API** (`bifrost-a.wbx2.com`) that exposes location data with additional configuration detail not available through the public API.
|
|
791
|
+
|
|
792
|
+
```python
|
|
793
|
+
Bifrost.__init__(self, org: wxcadm.Org, access_token: str)
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
Like CPAPI, this is normally instantiated automatically, not manually.
|
|
797
|
+
|
|
798
|
+
```python
|
|
799
|
+
Bifrost.get_location() -> list[dict]
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Retrieves all locations from the Bifrost API and **enriches** the corresponding wxcadm `Location` instances by setting `location.bifrost_config` on each matched location. This attaches the full Bifrost configuration dict to the Location object.
|
|
803
|
+
|
|
804
|
+
The exact contents of `bifrost_config` are not documented in the source. The `get_location()` method fetches from `bifrost-a.wbx2.com/api/v2/customers/{id}/locations`, paginates through all results, and assigns each raw location dict to the matching wxcadm `Location` object's `bifrost_config` attribute. The Bifrost module is exactly 52 lines with only the `get_location()` method.
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## What's Unique to wxcadm (Not in wxc_sdk)
|
|
809
|
+
|
|
810
|
+
| Capability | Module | Why It Matters |
|
|
811
|
+
|-----------|--------|---------------|
|
|
812
|
+
| **RedSky E911 management** | `redsky` | Full CRUD for buildings, locations, and all 4 network discovery types. No other Python library wraps the RedSky Horizon API. |
|
|
813
|
+
| **Meraki-to-RedSky audit** | `meraki` | Automated wire-map sync from Meraki Dashboard to RedSky. Tag-driven, supports simulate mode. |
|
|
814
|
+
| **CP-API operations** | `cpapi` | VM PIN reset, MOH file upload, AA greeting upload, workspace caller ID -- none available via public API. |
|
|
815
|
+
| **Service App lifecycle** | `applications` | Create, authorize, token exchange, secret rotation for Service Applications. |
|
|
816
|
+
| **Wholesale provisioning** | `wholesale` | Partner-level customer and subscriber management. |
|
|
817
|
+
| **Bifrost location enrichment** | `bifrost` | Internal location config not exposed publicly. |
|
|
818
|
+
|
|
819
|
+
---
|
|
820
|
+
|
|
821
|
+
## Gotchas
|
|
822
|
+
|
|
823
|
+
- **CPAPI.reset_vm_pin() has a confirmed race condition.** The method temporarily sets the org-wide global VM PIN, resets the user's PIN, then clears the global PIN. No locking exists; concurrent calls for multiple users will collide.
|
|
824
|
+
- **CPAPI.get_numbers() is deprecated.** The source code comment confirms: "This method is deprecated and will likely be removed eventually. The Webex API now supports a Numbers GET."
|
|
825
|
+
- **Service Apps marked as "Early Field Trial" in wxcadm source.** The `add_service_application()` docstring warns EFT status. Whether this has reached GA would require checking the live Webex developer portal.
|
|
826
|
+
- **Bifrost config contents are undocumented.** `location.bifrost_config` is populated by `Bifrost.get_location()` with the raw location dict from the Bifrost API. The exact keys depend on the Bifrost API response and are not documented in wxcadm source. The module is exactly 52 lines with a single method.
|
|
827
|
+
- **CP-API requires a special token scope.** Standard developer tokens may lack CP-API scope. Methods raise `TokenError` if the scope is missing — there is no graceful fallback.
|
|
828
|
+
- **RedSky is a separate auth domain.** RedSky credentials (username/password for `api.wxc.e911cloud.com`) are completely independent from Webex tokens. The `add_building()` method only supports US locations and raises `ValueError` for non-US addresses.
|
|
829
|
+
|
|
830
|
+
---
|
|
831
|
+
|
|
832
|
+
## See Also
|
|
833
|
+
|
|
834
|
+
- [emergency-services.md](emergency-services.md) — wxc_sdk emergency services APIs for comparison with wxcadm's RedSky integration
|
|
835
|
+
- [wxcadm-locations.md](wxcadm-locations.md) — wxcadm location management including `enhanced_emergency_calling` and `set_enhanced_emergency_calling()` which interact with RedSky
|