wxcli 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) hide show
  1. wxcli/__init__.py +4 -0
  2. wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
  3. wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
  4. wxcli/_playbook/.claude/rules/cleanup.md +29 -0
  5. wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
  6. wxcli/_playbook/.claude/rules/org-health.md +14 -0
  7. wxcli/_playbook/.claude/settings.json +8 -0
  8. wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
  9. wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
  10. wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
  11. wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
  12. wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
  13. wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
  14. wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
  15. wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
  16. wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
  17. wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
  18. wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
  19. wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
  20. wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
  21. wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
  22. wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
  23. wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
  24. wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
  25. wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
  26. wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
  27. wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
  28. wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
  29. wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
  30. wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
  31. wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
  32. wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
  33. wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
  34. wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
  35. wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
  36. wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
  37. wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
  38. wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
  39. wxcli/_playbook/CLAUDE.md +373 -0
  40. wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
  41. wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
  42. wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
  43. wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
  44. wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
  45. wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
  46. wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
  47. wxcli/_playbook/docs/reference/admin-partner.md +357 -0
  48. wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
  49. wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
  50. wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
  51. wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
  52. wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
  53. wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
  54. wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
  55. wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
  56. wxcli/_playbook/docs/reference/authentication.md +965 -0
  57. wxcli/_playbook/docs/reference/call-control.md +1205 -0
  58. wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
  59. wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
  60. wxcli/_playbook/docs/reference/call-routing.md +2304 -0
  61. wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
  62. wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
  63. wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
  64. wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
  65. wxcli/_playbook/docs/reference/devices-core.md +1869 -0
  66. wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
  67. wxcli/_playbook/docs/reference/devices-platform.md +467 -0
  68. wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
  69. wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
  70. wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
  71. wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
  72. wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
  73. wxcli/_playbook/docs/reference/meetings-content.md +473 -0
  74. wxcli/_playbook/docs/reference/meetings-core.md +903 -0
  75. wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
  76. wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
  77. wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
  78. wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
  79. wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
  80. wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
  81. wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
  82. wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
  83. wxcli/_playbook/docs/reference/provisioning.md +1366 -0
  84. wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
  85. wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
  86. wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
  87. wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
  88. wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
  89. wxcli/_version.py +24 -0
  90. wxcli/auth.py +137 -0
  91. wxcli/commands/__init__.py +0 -0
  92. wxcli/commands/_registry.py +181 -0
  93. wxcli/commands/activation_email.py +103 -0
  94. wxcli/commands/admin_recordings.py +585 -0
  95. wxcli/commands/analytics.py +105 -0
  96. wxcli/commands/announcement_playlists.py +219 -0
  97. wxcli/commands/announcements.py +519 -0
  98. wxcli/commands/archive_users.py +36 -0
  99. wxcli/commands/attachment_actions.py +60 -0
  100. wxcli/commands/audit_events.py +83 -0
  101. wxcli/commands/authorizations.py +113 -0
  102. wxcli/commands/auto_attendant.py +642 -0
  103. wxcli/commands/broadworks_billing_reports.py +126 -0
  104. wxcli/commands/broadworks_enterprises.py +160 -0
  105. wxcli/commands/broadworks_subscribers.py +319 -0
  106. wxcli/commands/broadworks_workspaces.py +108 -0
  107. wxcli/commands/call_controls.py +1304 -0
  108. wxcli/commands/call_park.py +494 -0
  109. wxcli/commands/call_pickup.py +229 -0
  110. wxcli/commands/call_queue.py +1878 -0
  111. wxcli/commands/call_recording.py +707 -0
  112. wxcli/commands/call_routing.py +1610 -0
  113. wxcli/commands/call_settings_for_me_phase_5.py +212 -0
  114. wxcli/commands/caller_reputation.py +169 -0
  115. wxcli/commands/calling_service.py +305 -0
  116. wxcli/commands/cc_address_book.py +770 -0
  117. wxcli/commands/cc_agent_greetings.py +242 -0
  118. wxcli/commands/cc_agent_summaries.py +39 -0
  119. wxcli/commands/cc_agent_wellbeing.py +216 -0
  120. wxcli/commands/cc_agents.py +425 -0
  121. wxcli/commands/cc_ai_assistant.py +55 -0
  122. wxcli/commands/cc_ai_feature.py +294 -0
  123. wxcli/commands/cc_audio_files.py +149 -0
  124. wxcli/commands/cc_auto_csat.py +311 -0
  125. wxcli/commands/cc_aux_code.py +458 -0
  126. wxcli/commands/cc_business_hour.py +353 -0
  127. wxcli/commands/cc_call_monitoring.py +228 -0
  128. wxcli/commands/cc_callbacks.py +194 -0
  129. wxcli/commands/cc_campaign.py +212 -0
  130. wxcli/commands/cc_captures.py +39 -0
  131. wxcli/commands/cc_contact_list.py +169 -0
  132. wxcli/commands/cc_contact_number.py +295 -0
  133. wxcli/commands/cc_data_sources.py +235 -0
  134. wxcli/commands/cc_desktop_layout.py +380 -0
  135. wxcli/commands/cc_desktop_profile.py +322 -0
  136. wxcli/commands/cc_dial_number.py +511 -0
  137. wxcli/commands/cc_dial_plan.py +347 -0
  138. wxcli/commands/cc_dnc.py +101 -0
  139. wxcli/commands/cc_entry_point.py +545 -0
  140. wxcli/commands/cc_ewt.py +51 -0
  141. wxcli/commands/cc_flow.py +477 -0
  142. wxcli/commands/cc_global_vars.py +430 -0
  143. wxcli/commands/cc_holiday_list.py +335 -0
  144. wxcli/commands/cc_journey.py +1261 -0
  145. wxcli/commands/cc_legacy_flows.py +49 -0
  146. wxcli/commands/cc_multimedia_profile.py +436 -0
  147. wxcli/commands/cc_notification.py +51 -0
  148. wxcli/commands/cc_outdial_ani.py +604 -0
  149. wxcli/commands/cc_overrides.py +341 -0
  150. wxcli/commands/cc_queue.py +1083 -0
  151. wxcli/commands/cc_queue_stats.py +55 -0
  152. wxcli/commands/cc_realtime.py +51 -0
  153. wxcli/commands/cc_resource_collection.py +312 -0
  154. wxcli/commands/cc_search.py +46 -0
  155. wxcli/commands/cc_site.py +400 -0
  156. wxcli/commands/cc_skill.py +350 -0
  157. wxcli/commands/cc_skill_profile.py +289 -0
  158. wxcli/commands/cc_subscriptions.py +426 -0
  159. wxcli/commands/cc_summaries.py +130 -0
  160. wxcli/commands/cc_tasks.py +844 -0
  161. wxcli/commands/cc_team.py +325 -0
  162. wxcli/commands/cc_user_profiles.py +349 -0
  163. wxcli/commands/cc_users.py +701 -0
  164. wxcli/commands/cc_work_types.py +363 -0
  165. wxcli/commands/cdr.py +89 -0
  166. wxcli/commands/classifications.py +38 -0
  167. wxcli/commands/cleanup.py +1427 -0
  168. wxcli/commands/client_settings.py +72 -0
  169. wxcli/commands/conference.py +328 -0
  170. wxcli/commands/configure.py +72 -0
  171. wxcli/commands/converged_recordings.py +347 -0
  172. wxcli/commands/converged_recordings_export.py +310 -0
  173. wxcli/commands/cq_playlists.py +42 -0
  174. wxcli/commands/cucm.py +2794 -0
  175. wxcli/commands/cucm_config.py +129 -0
  176. wxcli/commands/customer_assist.py +373 -0
  177. wxcli/commands/data_sources.py +227 -0
  178. wxcli/commands/dect_devices.py +787 -0
  179. wxcli/commands/device_configurations.py +77 -0
  180. wxcli/commands/device_dynamic_settings.py +320 -0
  181. wxcli/commands/device_settings.py +1489 -0
  182. wxcli/commands/devices.py +280 -0
  183. wxcli/commands/domains.py +142 -0
  184. wxcli/commands/ecm.py +178 -0
  185. wxcli/commands/emergency_services.py +827 -0
  186. wxcli/commands/events.py +83 -0
  187. wxcli/commands/external_voicemail.py +52 -0
  188. wxcli/commands/groups.py +206 -0
  189. wxcli/commands/guest_management.py +76 -0
  190. wxcli/commands/hds.py +340 -0
  191. wxcli/commands/hot_desk.py +67 -0
  192. wxcli/commands/hot_desking_members.py +118 -0
  193. wxcli/commands/hot_desking_portal.py +124 -0
  194. wxcli/commands/hunt_group.py +577 -0
  195. wxcli/commands/hybrid_clusters.py +71 -0
  196. wxcli/commands/hybrid_connectors.py +67 -0
  197. wxcli/commands/identity_org.py +159 -0
  198. wxcli/commands/init_playbook.py +155 -0
  199. wxcli/commands/licenses.py +106 -0
  200. wxcli/commands/live_monitoring.py +40 -0
  201. wxcli/commands/location_call_handling.py +577 -0
  202. wxcli/commands/location_schedules.py +341 -0
  203. wxcli/commands/location_settings.py +1297 -0
  204. wxcli/commands/location_voicemail.py +494 -0
  205. wxcli/commands/locations.py +336 -0
  206. wxcli/commands/meeting_captions.py +110 -0
  207. wxcli/commands/meeting_chats.py +63 -0
  208. wxcli/commands/meeting_invitees.py +234 -0
  209. wxcli/commands/meeting_messages.py +28 -0
  210. wxcli/commands/meeting_participants.py +278 -0
  211. wxcli/commands/meeting_polls.py +111 -0
  212. wxcli/commands/meeting_preferences.py +515 -0
  213. wxcli/commands/meeting_qa.py +78 -0
  214. wxcli/commands/meeting_qualities.py +43 -0
  215. wxcli/commands/meeting_reports.py +101 -0
  216. wxcli/commands/meeting_session_types.py +105 -0
  217. wxcli/commands/meeting_site.py +57 -0
  218. wxcli/commands/meeting_slido.py +44 -0
  219. wxcli/commands/meeting_summaries.py +92 -0
  220. wxcli/commands/meeting_tracking_codes.py +233 -0
  221. wxcli/commands/meeting_transcripts.py +230 -0
  222. wxcli/commands/meetings.py +1997 -0
  223. wxcli/commands/memberships.py +164 -0
  224. wxcli/commands/messages.py +213 -0
  225. wxcli/commands/mode_management.py +244 -0
  226. wxcli/commands/my_call_settings.py +3577 -0
  227. wxcli/commands/numbers.py +405 -0
  228. wxcli/commands/operating_modes.py +410 -0
  229. wxcli/commands/org_contacts.py +284 -0
  230. wxcli/commands/org_health_cli.py +55 -0
  231. wxcli/commands/org_settings.py +71 -0
  232. wxcli/commands/organizations.py +83 -0
  233. wxcli/commands/paging_group.py +257 -0
  234. wxcli/commands/partner_admins.py +141 -0
  235. wxcli/commands/partner_reports.py +188 -0
  236. wxcli/commands/partner_tags.py +216 -0
  237. wxcli/commands/people.py +279 -0
  238. wxcli/commands/person_call_settings.py +71 -0
  239. wxcli/commands/pstn.py +280 -0
  240. wxcli/commands/recording_report.py +154 -0
  241. wxcli/commands/report_templates.py +38 -0
  242. wxcli/commands/reports.py +144 -0
  243. wxcli/commands/resource_group_memberships.py +160 -0
  244. wxcli/commands/resource_groups.py +67 -0
  245. wxcli/commands/roles.py +63 -0
  246. wxcli/commands/room_tabs.py +160 -0
  247. wxcli/commands/rooms.py +225 -0
  248. wxcli/commands/scim_bulk.py +46 -0
  249. wxcli/commands/scim_groups.py +246 -0
  250. wxcli/commands/scim_schemas.py +82 -0
  251. wxcli/commands/scim_users.py +283 -0
  252. wxcli/commands/security_audit.py +54 -0
  253. wxcli/commands/service_apps.py +51 -0
  254. wxcli/commands/single_number_reach.py +209 -0
  255. wxcli/commands/team_memberships.py +155 -0
  256. wxcli/commands/teams.py +153 -0
  257. wxcli/commands/ucm_profile.py +42 -0
  258. wxcli/commands/update.py +219 -0
  259. wxcli/commands/user_settings.py +5116 -0
  260. wxcli/commands/video_mesh.py +1090 -0
  261. wxcli/commands/virtual_extensions.py +502 -0
  262. wxcli/commands/virtual_line_settings.py +2072 -0
  263. wxcli/commands/webhooks.py +176 -0
  264. wxcli/commands/wholesale_billing_reports.py +147 -0
  265. wxcli/commands/wholesale_provisioning.py +523 -0
  266. wxcli/commands/workspace_locations.py +339 -0
  267. wxcli/commands/workspace_metrics.py +103 -0
  268. wxcli/commands/workspace_personalization.py +70 -0
  269. wxcli/commands/workspace_settings.py +3347 -0
  270. wxcli/commands/workspaces.py +279 -0
  271. wxcli/commands/xapi.py +114 -0
  272. wxcli/config.py +139 -0
  273. wxcli/errors.py +77 -0
  274. wxcli/main.py +210 -0
  275. wxcli/migration/CLAUDE.md +106 -0
  276. wxcli/migration/__init__.py +1 -0
  277. wxcli/migration/advisory/CLAUDE.md +196 -0
  278. wxcli/migration/advisory/__init__.py +46 -0
  279. wxcli/migration/advisory/advisor.py +107 -0
  280. wxcli/migration/advisory/advisory_patterns.py +2552 -0
  281. wxcli/migration/advisory/recommendation_rules.py +835 -0
  282. wxcli/migration/cucm/CLAUDE.md +74 -0
  283. wxcli/migration/cucm/__init__.py +5 -0
  284. wxcli/migration/cucm/connection.py +265 -0
  285. wxcli/migration/cucm/discovery.py +225 -0
  286. wxcli/migration/cucm/extractors/__init__.py +1 -0
  287. wxcli/migration/cucm/extractors/announcements.py +92 -0
  288. wxcli/migration/cucm/extractors/base.py +90 -0
  289. wxcli/migration/cucm/extractors/device_profiles.py +98 -0
  290. wxcli/migration/cucm/extractors/devices.py +193 -0
  291. wxcli/migration/cucm/extractors/e911.py +103 -0
  292. wxcli/migration/cucm/extractors/features.py +309 -0
  293. wxcli/migration/cucm/extractors/helpers.py +73 -0
  294. wxcli/migration/cucm/extractors/informational.py +295 -0
  295. wxcli/migration/cucm/extractors/locations.py +221 -0
  296. wxcli/migration/cucm/extractors/moh.py +100 -0
  297. wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
  298. wxcli/migration/cucm/extractors/routing.py +493 -0
  299. wxcli/migration/cucm/extractors/shared_lines.py +146 -0
  300. wxcli/migration/cucm/extractors/templates.py +229 -0
  301. wxcli/migration/cucm/extractors/tier4.py +173 -0
  302. wxcli/migration/cucm/extractors/users.py +240 -0
  303. wxcli/migration/cucm/extractors/voicemail.py +243 -0
  304. wxcli/migration/cucm/extractors/workspaces.py +88 -0
  305. wxcli/migration/cucm/unity_connection.py +304 -0
  306. wxcli/migration/execute/CLAUDE.md +372 -0
  307. wxcli/migration/execute/__init__.py +303 -0
  308. wxcli/migration/execute/batch.py +277 -0
  309. wxcli/migration/execute/dependency.py +483 -0
  310. wxcli/migration/execute/engine.py +964 -0
  311. wxcli/migration/execute/handlers.py +2232 -0
  312. wxcli/migration/execute/planner.py +1939 -0
  313. wxcli/migration/execute/runtime.py +571 -0
  314. wxcli/migration/export/CLAUDE.md +49 -0
  315. wxcli/migration/export/__init__.py +8 -0
  316. wxcli/migration/export/csv_export.py +126 -0
  317. wxcli/migration/export/deployment_plan.py +479 -0
  318. wxcli/migration/export/json_export.py +60 -0
  319. wxcli/migration/models.py +941 -0
  320. wxcli/migration/phone_models.py +50 -0
  321. wxcli/migration/preflight/CLAUDE.md +52 -0
  322. wxcli/migration/preflight/__init__.py +89 -0
  323. wxcli/migration/preflight/checks.py +911 -0
  324. wxcli/migration/preflight/runner.py +293 -0
  325. wxcli/migration/rate_limiter.py +144 -0
  326. wxcli/migration/report/CLAUDE.md +245 -0
  327. wxcli/migration/report/__init__.py +7 -0
  328. wxcli/migration/report/appendix.py +2437 -0
  329. wxcli/migration/report/assembler.py +183 -0
  330. wxcli/migration/report/charts.py +263 -0
  331. wxcli/migration/report/executive.py +776 -0
  332. wxcli/migration/report/explainer.py +704 -0
  333. wxcli/migration/report/helpers.py +74 -0
  334. wxcli/migration/report/ingest.py +152 -0
  335. wxcli/migration/report/notice_templates.py +192 -0
  336. wxcli/migration/report/score.py +375 -0
  337. wxcli/migration/report/styles.py +1052 -0
  338. wxcli/migration/report/user_diff.py +1052 -0
  339. wxcli/migration/report/user_notice.py +425 -0
  340. wxcli/migration/state.py +139 -0
  341. wxcli/migration/store.py +860 -0
  342. wxcli/migration/transform/CLAUDE.md +107 -0
  343. wxcli/migration/transform/__init__.py +12 -0
  344. wxcli/migration/transform/analysis_pipeline.py +453 -0
  345. wxcli/migration/transform/analyzers/__init__.py +142 -0
  346. wxcli/migration/transform/analyzers/css_permission.py +189 -0
  347. wxcli/migration/transform/analyzers/css_routing.py +335 -0
  348. wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
  349. wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
  350. wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
  351. wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
  352. wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
  353. wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
  354. wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
  355. wxcli/migration/transform/analyzers/missing_data.py +300 -0
  356. wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
  357. wxcli/migration/transform/analyzers/shared_line.py +170 -0
  358. wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
  359. wxcli/migration/transform/analyzers/workspace_license.py +212 -0
  360. wxcli/migration/transform/cross_reference.py +1136 -0
  361. wxcli/migration/transform/cucm_pattern.py +246 -0
  362. wxcli/migration/transform/decisions.py +301 -0
  363. wxcli/migration/transform/e164.py +145 -0
  364. wxcli/migration/transform/engine.py +256 -0
  365. wxcli/migration/transform/mappers/CLAUDE.md +123 -0
  366. wxcli/migration/transform/mappers/__init__.py +76 -0
  367. wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
  368. wxcli/migration/transform/mappers/base.py +190 -0
  369. wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
  370. wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
  371. wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
  372. wxcli/migration/transform/mappers/css_mapper.py +868 -0
  373. wxcli/migration/transform/mappers/dect_mapper.py +367 -0
  374. wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
  375. wxcli/migration/transform/mappers/device_mapper.py +227 -0
  376. wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
  377. wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
  378. wxcli/migration/transform/mappers/e911_mapper.py +129 -0
  379. wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
  380. wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
  381. wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
  382. wxcli/migration/transform/mappers/line_mapper.py +310 -0
  383. wxcli/migration/transform/mappers/location_mapper.py +235 -0
  384. wxcli/migration/transform/mappers/moh_mapper.py +114 -0
  385. wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
  386. wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
  387. wxcli/migration/transform/mappers/routing_mapper.py +821 -0
  388. wxcli/migration/transform/mappers/snr_mapper.py +201 -0
  389. wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
  390. wxcli/migration/transform/mappers/user_mapper.py +264 -0
  391. wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
  392. wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
  393. wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
  394. wxcli/migration/transform/normalizers.py +2008 -0
  395. wxcli/migration/transform/pattern_converter.py +106 -0
  396. wxcli/migration/transform/pipeline.py +271 -0
  397. wxcli/migration/transform/rules.py +255 -0
  398. wxcli/org_health/CLAUDE.md +63 -0
  399. wxcli/org_health/__init__.py +7 -0
  400. wxcli/org_health/analyze.py +93 -0
  401. wxcli/org_health/checks.py +458 -0
  402. wxcli/org_health/collector.py +62 -0
  403. wxcli/org_health/models.py +78 -0
  404. wxcli/org_health/report.py +300 -0
  405. wxcli/output.py +101 -0
  406. wxcli-1.2.0.dist-info/METADATA +505 -0
  407. wxcli-1.2.0.dist-info/RECORD +413 -0
  408. wxcli-1.2.0.dist-info/WHEEL +5 -0
  409. wxcli-1.2.0.dist-info/entry_points.txt +2 -0
  410. wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
  411. wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
  412. wxcli-1.2.0.dist-info/scm_version.json +8 -0
  413. wxcli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1262 @@
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. RouteLists.create(), CallRouting(org) signature, Webhooks(org) signature, PSTNProviderList/LocationPSTN parent→location renaming, NumberList(org, location) signature — all accurately documented. No discrepancies found. -->
3
+ # wxcadm -- Routing, PSTN, CDR, Reports, Jobs, Webhooks
4
+
5
+ Reference for the secondary wxcadm modules that handle call routing infrastructure, PSTN configuration, call detail records, reporting, asynchronous jobs, and webhooks.
6
+
7
+ Source files:
8
+ - `wxcadm/call_routing.py` -- dial plans, trunks, route groups, route lists, translation patterns
9
+ - `wxcadm/pstn.py` -- PSTN provider config per location
10
+ - `wxcadm/cdr.py` -- call detail record processing
11
+ - `wxcadm/reports.py` -- report generation and retrieval
12
+ - `wxcadm/jobs.py` -- async job management (number moves, user moves, phone rebuilds)
13
+ - `wxcadm/webhooks.py` -- webhook CRUD
14
+
15
+ ---
16
+
17
+ <!-- Updated by playbook session 2026-03-18 -->
18
+ ## When to Use wxcadm vs Raw HTTP
19
+
20
+ | Use wxcadm when | Use raw HTTP when |
21
+ |---|---|
22
+ | CDR post-processing -- the `Call -> CallLeg -> LegPart` hierarchy with automatic transfer merging is unique to wxcadm and has no raw HTTP equivalent | Call routing CRUD (trunks, route groups, route lists, dial plans, translation patterns) |
23
+ | You need the two-phase validate-then-move pattern for `UserMoveJobList` in a single call | PSTN provider configuration per location |
24
+ | Object-graph navigation for routing (e.g., `org.call_routing.trunks.get(name=...)`) is more convenient than tracking IDs | Report creation, job management, webhook CRUD |
25
+
26
+ > **Note:** The playbook uses raw HTTP via `api.session.rest_*()` for standard CRUD operations. All routing, PSTN, report, job, and webhook endpoints are standard Webex REST APIs that work fine with raw HTTP. wxcadm's unique value here is the CDR parsing module (`CallDetailRecords`) which builds a structured call hierarchy from raw CSV records.
27
+
28
+ ---
29
+
30
+ ## Table of Contents
31
+
32
+ 1. [Call Routing](#1-call-routing)
33
+ - [CallRouting (entry point)](#callrouting-entry-point)
34
+ - [Trunks / Trunk](#trunks--trunk)
35
+ - [RouteGroups / RouteGroup](#routegroups--routegroup)
36
+ - [RouteLists / RouteList](#routelists--routelist)
37
+ - [DialPlans / DialPlan](#dialplans--dialplan)
38
+ - [TranslationPatternList / TranslationPattern](#translationpatternlist--translationpattern)
39
+ 2. [PSTN Configuration](#2-pstn-configuration)
40
+ 3. [Call Detail Records (CDR)](#3-call-detail-records-cdr)
41
+ 4. [Reports](#4-reports)
42
+ 5. [Jobs](#5-jobs)
43
+ - [Number Management Jobs](#number-management-jobs)
44
+ - [User Move Jobs](#user-move-jobs)
45
+ - [Rebuild Phones Jobs](#rebuild-phones-jobs)
46
+ 6. [Webhooks](#6-webhooks)
47
+ 7. [wxcadm vs wxc_sdk Comparison](#7-wxcadm-vs-wxc_sdk-comparison)
48
+
49
+ ---
50
+
51
+ ## 1. Call Routing
52
+
53
+ All call routing objects live under `org.call_routing`, which returns a `CallRouting` instance. The API base path for premise PSTN routing is `/v1/telephony/config/premisePstn/`. Translation patterns use `/v1/telephony/config/callRouting/translationPatterns`.
54
+
55
+ ### CallRouting (entry point)
56
+
57
+ ```python
58
+ class CallRouting:
59
+ def __init__(self, org: wxcadm.Org)
60
+ ```
61
+
62
+ **Properties** (each returns a freshly-initialized collection -- no caching):
63
+
64
+ | Property | Returns | Description |
65
+ |----------|---------|-------------|
66
+ | `trunks` | `Trunks` | All trunks in the org |
67
+ | `route_groups` | `RouteGroups` | All route groups in the org |
68
+ | `route_lists` | `RouteLists` | All route lists in the org |
69
+ | `dial_plans` | `DialPlans` | All dial plans in the org |
70
+
71
+ **Methods:**
72
+
73
+ ```python
74
+ def test(
75
+ self,
76
+ originator: Person | Trunk,
77
+ destination: str,
78
+ orig_number: Optional[str] = None
79
+ ) -> dict
80
+ ```
81
+
82
+ Tests call routing for a given originator and destination. The `originator` must be a `Person` or `Trunk` instance -- the method determines `originatorType` (`USER` or `TRUNK`) automatically. `orig_number` is only used for `TRUNK` originators. Returns the raw API response dict because the response format varies by routing outcome.
83
+
84
+ API endpoint: `POST /v1/telephony/config/actions/testCallRouting/invoke`
85
+
86
+ ---
87
+
88
+ ### Trunks / Trunk
89
+
90
+ #### Trunks (collection)
91
+
92
+ ```python
93
+ class Trunks(UserList):
94
+ def __init__(self, org: wxcadm.Org)
95
+ ```
96
+
97
+ Fetches all trunks on init from `GET /v1/telephony/config/premisePstn/trunks` (items key: `trunks`). Iterable list of `Trunk` instances.
98
+
99
+ **Methods:**
100
+
101
+ ```python
102
+ def get(
103
+ self,
104
+ name: Optional[str] = None,
105
+ id: Optional[str] = None
106
+ ) -> Optional[Trunk]
107
+ ```
108
+
109
+ Find a trunk by name or ID. Raises `ValueError` if both are `None`.
110
+
111
+ ```python
112
+ def add_trunk(
113
+ self,
114
+ name: str,
115
+ location: Location | str,
116
+ password: str,
117
+ dual_identity_support: bool,
118
+ type: str, # 'REGISTERING' or 'CERTIFICATE_BASED'
119
+ device_type: Optional[str] = 'Cisco Unified Border Element',
120
+ address: Optional[str] = None, # required for certificate-based
121
+ domain: Optional[str] = None, # required for certificate-based
122
+ port: Optional[int] = None, # required for certificate-based
123
+ max_concurrent_calls: Optional[int] = None # required for certificate-based
124
+ ) -> bool
125
+ ```
126
+
127
+ Creates a new trunk. For `CERTIFICATE_BASED` type, the optional parameters (`address`, `domain`, `port`, `max_concurrent_calls`) are required. Returns `True` on success, `False` otherwise.
128
+
129
+ #### Trunk (dataclass)
130
+
131
+ ```python
132
+ @dataclass_json
133
+ @dataclass
134
+ class Trunk:
135
+ org: wxcadm.Org
136
+ id: str
137
+ name: str
138
+ location: dict | Location # resolved to Location in __post_init__
139
+ in_use: bool # JSON field: "inUse"
140
+ trunk_type: str # JSON field: "trunkType" -- 'REGISTERING' or 'CERTIFICATE_BASED'
141
+ dedicated_instance_only: bool # JSON field: "isRestrictedToDedicatedInstance"
142
+ ```
143
+
144
+ **Lazy-loaded detail fields** (populated on first attribute access via `__getattr__`, triggering `GET /v1/telephony/config/premisePstn/trunks/{id}`):
145
+
146
+ | Attribute | Type | Description |
147
+ |-----------|------|-------------|
148
+ | `otg_dtg` | `str` | OTG/DTG identifier |
149
+ | `lineport` | `str` | Line/Port identifier |
150
+ | `used_by_locations` | `list[dict]` | Locations using this trunk for PSTN |
151
+ | `pilot_user_id` | `str` | Pilot user ID |
152
+ | `outbound_proxy` | `OutboundProxy | None` | Outbound proxy config |
153
+ | `sip_auth_user` | `str` | SIP auth username |
154
+ | `status` | `str` | Trunk status |
155
+ | `response_status` | `list` | Status messages |
156
+ | `dual_identity_support` | `bool` | Dual identity flag |
157
+ | `device_type` | `str` | Device type |
158
+ | `max_calls` | `int` | Max concurrent calls |
159
+
160
+ **Methods:**
161
+
162
+ ```python
163
+ def set_dual_identity_support(self, enabled: bool) -> bool
164
+ ```
165
+
166
+ Sets the dual identity support flag. Returns `True` on success. Raises `wxcadm.ApiError` on API failure.
167
+
168
+ ```python
169
+ def set_max_calls(self, calls: int) -> bool
170
+ ```
171
+
172
+ Sets max concurrent calls. Only supported for `CERTIFICATE_BASED` trunks. Raises `ValueError` for registering trunks.
173
+
174
+ ---
175
+
176
+ ### RouteGroups / RouteGroup
177
+
178
+ #### RouteGroups (collection)
179
+
180
+ ```python
181
+ class RouteGroups(UserList):
182
+ def __init__(self, org: wxcadm.Org)
183
+ ```
184
+
185
+ Fetches from `GET /v1/telephony/config/premisePstn/routeGroups` (items key: `routeGroups`).
186
+
187
+ **Methods:**
188
+
189
+ ```python
190
+ def get(
191
+ self,
192
+ id: Optional[str] = None,
193
+ name: Optional[str] = None
194
+ ) -> Optional[RouteGroup]
195
+ ```
196
+
197
+ Find a route group by ID or name.
198
+
199
+ #### RouteGroup (dataclass)
200
+
201
+ ```python
202
+ @dataclass
203
+ class RouteGroup:
204
+ org: wxcadm.Org
205
+ id: str
206
+ name: str
207
+ inUse: bool # whether the route group is used by any location, route list, or dial plan
208
+ ```
209
+
210
+ **Properties:**
211
+
212
+ ```python
213
+ @property
214
+ def trunks(self) -> List[dict]
215
+ ```
216
+
217
+ Returns the trunks within the route group with their priorities. Each dict contains trunk details and a `priority` key. Fetches from `GET /v1/telephony/config/premisePstn/routeGroups/{id}` (items key: `localGateways`).
218
+
219
+ **Methods:**
220
+
221
+ ```python
222
+ def add_trunk(
223
+ self,
224
+ trunk: Trunk,
225
+ priority: Union[str, int] # int, or 'next' / 'with_last'
226
+ ) -> bool
227
+ ```
228
+
229
+ Adds a trunk to the route group. The `priority` parameter accepts:
230
+ - An `int` for a specific priority value
231
+ - `'next'` to set priority one higher than the current highest
232
+ - `'with_last'` to match the current highest priority (equal weight)
233
+
234
+ Returns `True` on success.
235
+
236
+ ---
237
+
238
+ ### RouteLists / RouteList
239
+
240
+ #### RouteLists (collection)
241
+
242
+ ```python
243
+ class RouteLists(UserList):
244
+ def __init__(self, org: wxcadm.Org)
245
+ ```
246
+
247
+ Fetches from `GET /v1/telephony/config/premisePstn/routeLists` (items key: `routeLists`).
248
+
249
+ **Methods:**
250
+
251
+ ```python
252
+ def create(
253
+ self,
254
+ location: Location,
255
+ route_group: RouteGroup,
256
+ name: str
257
+ ) -> RouteList
258
+ ```
259
+
260
+ Creates a new route list and returns the `RouteList` instance.
261
+
262
+ ```python
263
+ def get(
264
+ self,
265
+ name: Optional[str] = None,
266
+ id: Optional[str] = None
267
+ ) -> Optional[RouteList]
268
+ ```
269
+
270
+ Find a route list by name or ID. Raises `ValueError` if both are `None`.
271
+
272
+ #### RouteList (dataclass)
273
+
274
+ ```python
275
+ @dataclass
276
+ class RouteList:
277
+ org: wxcadm.Org
278
+ id: str
279
+ name: str
280
+ locationId: str # resolved to Location instance in __post_init__
281
+ locationName: str # deleted after __post_init__
282
+ routeGroupId: str # resolved to RouteGroup instance in __post_init__
283
+ routeGroupName: str # deleted after __post_init__
284
+ ```
285
+
286
+ After `__post_init__`, the raw ID/name fields are replaced with:
287
+ - `self.location` -- resolved `Location` instance
288
+ - `self.route_group` -- resolved `RouteGroup` instance
289
+
290
+ **Bug:** `__post_init__` calls `self.org.call_routing.route_groups.get_route_group(id=...)` but the `RouteGroups` class only defines `get()`, not `get_route_group()`. This will raise `AttributeError` at runtime when a `RouteList` is instantiated. Workaround: iterate `route_groups` manually or call `.get(id=...)` instead.
291
+
292
+ **Properties:**
293
+
294
+ ```python
295
+ @property
296
+ def numbers(self)
297
+ ```
298
+
299
+ Returns the numbers assigned to this route list. Fetches from `GET /v1/telephony/config/premisePstn/routeLists/{id}/numbers`.
300
+
301
+ **Bug:** The `numbers` property calls `response.json()` on the return value of `org.api.get()`, but `org.api.get()` already returns a parsed dict (not a raw `Response` object). This will raise `AttributeError: 'dict' object has no attribute 'json'` at runtime. Workaround: use raw HTTP `GET /v1/telephony/config/premisePstn/routeLists/{id}/numbers` instead.
302
+
303
+ **Not implemented:** Delete route list (noted as TODO in source).
304
+
305
+ ---
306
+
307
+ ### DialPlans / DialPlan
308
+
309
+ #### DialPlans (collection)
310
+
311
+ ```python
312
+ class DialPlans(UserList):
313
+ def __init__(self, org: wxcadm.Org)
314
+ ```
315
+
316
+ Fetches from `GET /v1/telephony/config/premisePstn/dialPlans` (items key: `dialPlans`).
317
+
318
+ No `get()` method is defined on the collection. To find a specific dial plan, iterate manually:
319
+
320
+ ```python
321
+ dp = next((d for d in org.call_routing.dial_plans if d.name == "My Plan"), None)
322
+ ```
323
+
324
+ #### DialPlan (dataclass)
325
+
326
+ ```python
327
+ @dataclass
328
+ class DialPlan:
329
+ org: wxcadm.Org
330
+ id: str
331
+ name: str
332
+ routeId: str # ID of the route (trunk, route group, or route list)
333
+ routeName: str # name of the route
334
+ routeType: str # type of the route
335
+ ```
336
+
337
+ **Properties:**
338
+
339
+ ```python
340
+ @property
341
+ def patterns(self) -> list
342
+ ```
343
+
344
+ Returns the dial patterns within the dial plan. Fetches from `GET /v1/telephony/config/premisePstn/dialPlans/{id}/dialPatterns` (items key: `dialPatterns`).
345
+
346
+ **Methods:**
347
+
348
+ ```python
349
+ def add_pattern(self, pattern: str) -> bool
350
+ ```
351
+
352
+ Adds a dial pattern. Sends `PUT` with action `ADD`. Returns `True` on success.
353
+
354
+ ```python
355
+ def delete_pattern(self, pattern: str) -> bool
356
+ ```
357
+
358
+ Deletes an existing dial pattern. Sends `PUT` with action `DELETE`. Returns `True` on success.
359
+
360
+ ---
361
+
362
+ ### TranslationPatternList / TranslationPattern
363
+
364
+ Translation patterns can exist at org level or location level.
365
+
366
+ #### TranslationPatternList (collection)
367
+
368
+ ```python
369
+ class TranslationPatternList(UserList):
370
+ def __init__(
371
+ self,
372
+ org: wxcadm.Org,
373
+ location: Optional[Location] = None
374
+ )
375
+ ```
376
+
377
+ If `location` is provided, fetches patterns scoped to that location via `limitToLocationId` param. Otherwise fetches all org-level patterns.
378
+
379
+ API endpoint: `GET /v1/telephony/config/callRouting/translationPatterns`
380
+
381
+ **Methods:**
382
+
383
+ ```python
384
+ def refresh(self) -> list
385
+ ```
386
+
387
+ Re-fetches the list from the API. Returns the updated data list.
388
+
389
+ ```python
390
+ def get(
391
+ self,
392
+ id: Optional[str] = None,
393
+ name: Optional[str] = None,
394
+ match_pattern: Optional[str] = None,
395
+ replacement_pattern: Optional[str] = None
396
+ ) -> Optional[TranslationPattern]
397
+ ```
398
+
399
+ Finds a translation pattern matching ALL provided criteria (AND logic). Any parameter left as `None` is treated as a wildcard.
400
+
401
+ ```python
402
+ def create(
403
+ self,
404
+ name: str,
405
+ match_pattern: str,
406
+ replacement_pattern: str,
407
+ location: Optional[Location] = None
408
+ ) -> TranslationPattern
409
+ ```
410
+
411
+ Creates a new translation pattern. Scope logic:
412
+ - If `location` argument is provided, creates at that location's scope
413
+ - If no `location` argument but the list was initialized with a location, uses the list's location
414
+ - Otherwise creates at org scope
415
+
416
+ Returns the newly created `TranslationPattern` instance (fetched back from API after creation).
417
+
418
+ #### TranslationPattern
419
+
420
+ ```python
421
+ class TranslationPattern:
422
+ def __init__(
423
+ self,
424
+ org: wxcadm.Org,
425
+ location: Optional[Location] = None,
426
+ config: Optional[dict] = None
427
+ )
428
+ ```
429
+
430
+ **Attributes:**
431
+
432
+ | Attribute | Type | Description |
433
+ |-----------|------|-------------|
434
+ | `id` | `str` | Unique identifier |
435
+ | `name` | `str` | Pattern name |
436
+ | `match_pattern` | `str` | The pattern to match (from `matchingPattern`) |
437
+ | `replacement_pattern` | `str` | The replacement (from `replacementPattern`) |
438
+ | `location` | `Location | None` | Associated location, or `None` for org-level |
439
+ | `level` | `str | None` | `'Location'` or `None` |
440
+
441
+ **Methods:**
442
+
443
+ ```python
444
+ def update(
445
+ self,
446
+ name: Optional[str] = None,
447
+ match_pattern: Optional[str] = None,
448
+ replacement_pattern: Optional[str] = None
449
+ ) -> bool
450
+ ```
451
+
452
+ Updates the translation pattern. Only provided fields are changed; `None` fields keep current values. Uses location-scoped or org-scoped URL based on `self.location`. Returns `True` on success.
453
+
454
+ ```python
455
+ def delete(self) -> bool
456
+ ```
457
+
458
+ Deletes the translation pattern. Returns `True` on success.
459
+
460
+ ---
461
+
462
+ ## 2. PSTN Configuration
463
+
464
+ PSTN config is location-scoped, accessed via `LocationPSTN`.
465
+
466
+ ### LocationPSTN
467
+
468
+ ```python
469
+ class LocationPSTN:
470
+ def __init__(self, location: wxcadm.Location)
471
+ ```
472
+
473
+ On init, fetches the current PSTN connection for the location from `GET /v1/telephony/pstn/locations/{id}/connection`. If no PSTN is configured, `self.provider` is set to `None` (does not raise).
474
+
475
+ **Attributes:**
476
+
477
+ | Attribute | Type | Description |
478
+ |-----------|------|-------------|
479
+ | `location` | `Location` | The associated location |
480
+ | `available_providers` | `PSTNProviderList` | Available PSTN providers for this location |
481
+ | `provider` | `PSTNProvider | None` | Currently configured provider |
482
+ | `type` | `str` | PSTN connection type (e.g. `pstnConnectionType` value) |
483
+
484
+ **Methods:**
485
+
486
+ ```python
487
+ def set_provider(
488
+ self,
489
+ provider: PSTNProvider | str | RouteGroup | Trunk
490
+ ) -> bool
491
+ ```
492
+
493
+ Sets the PSTN provider for the location. Accepts:
494
+ - `PSTNProvider` instance -- sets by provider ID
495
+ - `str` -- looked up as a provider ID in `available_providers`
496
+ - `Trunk` -- sets `premiseRouteType: 'TRUNK'` with the trunk ID
497
+ - `RouteGroup` -- sets `premiseRouteType: 'ROUTE_GROUP'` with the route group ID
498
+
499
+ Returns `True` on success.
500
+
501
+ API endpoint: `PUT /v1/telephony/pstn/locations/{id}/connection`
502
+
503
+ ### PSTNProvider (dataclass)
504
+
505
+ ```python
506
+ @dataclass_json
507
+ @dataclass
508
+ class PSTNProvider:
509
+ id: str
510
+ name: str # JSON field: "displayName"
511
+ services: list # JSON field: "pstnServices"
512
+ ```
513
+
514
+ ### PSTNProviderList (collection)
515
+
516
+ ```python
517
+ class PSTNProviderList(UserList):
518
+ def __init__(self, location: wxcadm.Location)
519
+ ```
520
+
521
+ Fetches available connection options from `GET /v1/telephony/pstn/locations/{id}/connectionOptions`.
522
+
523
+ **Methods:**
524
+
525
+ ```python
526
+ def get(
527
+ self,
528
+ id: Optional[str] = None,
529
+ name: Optional[str] = None
530
+ ) -> Optional[PSTNProvider]
531
+ ```
532
+
533
+ Find a provider by ID or name.
534
+
535
+ ---
536
+
537
+ ## 3. Call Detail Records (CDR)
538
+
539
+ The CDR module processes raw CDR records into a structured call hierarchy. **It does not fetch CDRs from the API directly** -- records must be obtained externally (typically via the Reports module's `cdr_report()` method) and passed in as a list of dicts with raw CSV field names.
540
+
541
+ ### Data Model Hierarchy
542
+
543
+ ```
544
+ CallDetailRecords
545
+ -> calls: list[Call]
546
+ -> legs: list[CallLeg]
547
+ -> parts: list[LegPart]
548
+ ```
549
+
550
+ ### CallDetailRecords (entry point)
551
+
552
+ ```python
553
+ class CallDetailRecords:
554
+ def __init__(
555
+ self,
556
+ records: list, # list of dicts with raw CDR field names
557
+ webex: Optional[wxcadm.Webex] = None # enables user/endpoint lookup
558
+ )
559
+ ```
560
+
561
+ On init, processes all records into `Call` objects and merges transferred calls based on matching part IDs within a 24-hour window.
562
+
563
+ **Attributes:**
564
+
565
+ | Attribute | Type | Description |
566
+ |-----------|------|-------------|
567
+ | `records` | `list` | The raw records passed in |
568
+ | `webex` | `Webex | None` | Webex connection for resolving user IDs |
569
+ | `calls` | `list[Call]` | Unordered list of processed calls |
570
+
571
+ **Properties:**
572
+
573
+ ```python
574
+ @property
575
+ def calls_sorted(self) -> list[Call]
576
+ ```
577
+
578
+ Returns calls sorted by start time (ascending).
579
+
580
+ **Methods:**
581
+
582
+ ```python
583
+ def get_call_by_correlation_id(self, correlation_id: str) -> Optional[Call]
584
+ ```
585
+
586
+ ```python
587
+ def get_call_by_part_call_id(self, part_call_id: str) -> Optional[Call]
588
+ ```
589
+
590
+ ```python
591
+ def get_abandoned_calls(self) -> list[Call]
592
+ ```
593
+
594
+ Returns calls where the caller hung up in a call queue before an agent answered.
595
+
596
+ ```python
597
+ def get_pstn_calls(self) -> list[Call]
598
+ ```
599
+
600
+ Returns calls with at least one PSTN (off-net) leg.
601
+
602
+ ```python
603
+ def get_voicemail_deposit_calls(self) -> list[Call]
604
+ ```
605
+
606
+ Returns calls where the caller was sent to voicemail.
607
+
608
+ ### Call
609
+
610
+ ```python
611
+ class Call:
612
+ def __init__(self, correlation_id: str)
613
+ ```
614
+
615
+ **Attributes:**
616
+ - `id` (`str`) -- the correlation ID
617
+ - `legs` (`list[CallLeg]`) -- the call legs
618
+ - `correlation_ids` (`list[str]`) -- all correlation IDs (includes merged transfers)
619
+
620
+ **Properties:**
621
+
622
+ | Property | Type | Description |
623
+ |----------|------|-------------|
624
+ | `start_time` | `datetime` | Earliest leg start time |
625
+ | `end_time` | `datetime` | Latest leg end time |
626
+ | `duration` | `str` | Formatted duration (HH:MM:SS) |
627
+ | `calling_number` | `str` | Calling number from the first sorted leg |
628
+ | `leg_count` | `int` | Number of legs |
629
+ | `legs_sorted` | `list[CallLeg]` | Legs sorted by start time |
630
+ | `part_ids` | `set` | All part IDs in the call |
631
+ | `transfer_ids` | `list` | All transfer-related call IDs |
632
+ | `answered_legs` | `int` | Count of answered legs |
633
+ | `has_queue_leg` | `bool` | Whether any leg involves a call queue |
634
+ | `has_pstn_leg` | `bool` | Whether any leg is PSTN |
635
+ | `has_vm_deposit_leg` | `bool` | Whether any leg is a voicemail deposit |
636
+ | `is_queue_abandon` | `bool` | Caller hung up in queue without agent answer |
637
+
638
+ **Methods:**
639
+
640
+ ```python
641
+ def add_record(self, record: dict)
642
+ ```
643
+
644
+ Adds a CDR record to the call, either to an existing leg (if part IDs match) or by creating a new leg.
645
+
646
+ ### CallLeg
647
+
648
+ ```python
649
+ class CallLeg:
650
+ def __init__(self)
651
+ ```
652
+
653
+ Contains `parts: list[LegPart]`.
654
+
655
+ **Properties:**
656
+
657
+ | Property | Type | Description |
658
+ |----------|------|-------------|
659
+ | `start_time` | `datetime` | First part's start time |
660
+ | `end_time` | `datetime` | End time (accounts for transfers) |
661
+ | `pstn_leg` | `bool` | Whether this leg is PSTN |
662
+ | `duration` | `int` | Duration in seconds |
663
+ | `ring_duration` | `int` | Ring time in seconds |
664
+ | `transfer_ids` | `list` | Transfer-related call IDs |
665
+ | `orig_part` | `LegPart | None` | Originating part |
666
+ | `term_part` | `LegPart | None` | Terminating part |
667
+ | `is_queue_leg` | `bool` | Terminating user type is `CALLCENTERPREMIUM` |
668
+ | `is_aa_leg` | `bool` | Terminating user type is `AUTOMATEDATTENDANTVIDEO` |
669
+ | `is_agent_leg` | `bool` | User type `USER` with redirect reason `CALLQUEUE` |
670
+ | `is_vm_deposit` | `bool` | Called line is `Voice Portal Voice Messaging Group` |
671
+ | `label` | `str` | Display label for the leg |
672
+ | `calling_number` | `str` | Calling party number |
673
+ | `leg_description` | `str` | "From X to Y" description |
674
+ | `new_leg_description` | `str` | Enhanced description with reasons |
675
+ | `in_reason` | `str | None` | Why the leg was received (e.g. `SIP Inbound`, redirect reason) |
676
+ | `out_reason` | `str | None` | Why the leg originated (e.g. `Queue Callback`) |
677
+ | `answered` | `bool` | Whether any part was answered |
678
+ | `answered_label` | `str` | `'Answered'` or `'Unanswered'` |
679
+ | `parts_id` | `list` | All local and remote IDs from parts |
680
+ | `recorded` | `bool` | Whether any part has a successful recording |
681
+
682
+ **Methods:**
683
+
684
+ ```python
685
+ def add_part(self, record: dict) -> LegPart
686
+ ```
687
+
688
+ ### LegPart
689
+
690
+ ```python
691
+ class LegPart:
692
+ def __init__(self, record: dict)
693
+ ```
694
+
695
+ Wraps a single CDR record. Key attributes parsed from the raw dict:
696
+
697
+ | Attribute | Source Field | Type |
698
+ |-----------|-------------|------|
699
+ | `start_time` | `Start time` | `datetime` |
700
+ | `end_time` | `Release time` | `datetime` |
701
+ | `answer_time` | `Answer time` | `datetime | None` |
702
+ | `answered` | `Answered` | `bool` |
703
+ | `local_id` | `Local call ID` | `str | None` |
704
+ | `remote_id` | `Remote call ID` | `str | None` |
705
+ | `direction` | `Direction` | `str` |
706
+ | `calling_line_id` | `Calling line ID` | `str` |
707
+ | `called_line_id` | `Called line ID` | `str` |
708
+ | `dialed_digits` | `Dialed digits` | `str` |
709
+ | `user_number` | `User number` | `str` |
710
+ | `called_number` | `Called number` | `str` |
711
+ | `calling_number` | `Calling number` | `str` |
712
+ | `redirecting_number` | `Redirecting number` | `str` |
713
+ | `location_name` | `Location` | `str` |
714
+ | `user_type` | `User type` | `str` |
715
+ | `user` | `User` | `str` |
716
+ | `call_type` | `Call type` | `str` |
717
+ | `duration` | `Duration` | `str` |
718
+ | `time_offset` | `Site timezone` | `str` |
719
+ | `releasing_party` | `Releasing party` | `str` |
720
+ | `original_reason` | `Original reason` | `str` |
721
+ | `redirect_reason` | `Redirect reason` | `str` |
722
+ | `related_reason` | `Related reason` | `str` |
723
+ | `outcome` | `Call outcome` | `str` |
724
+ | `outcome_reason` | `Call outcome reason` | `str` |
725
+ | `ring_duration` | `Ring duration` | `str` |
726
+ | `device_owner_uuid` | `Device owner UUID` | `str` |
727
+ | `recording_platform` | `Call Recording Platform Name` | `str` |
728
+ | `recording_result` | `Call Recording Result` | `str` |
729
+ | `recording_trigger` | `Call Recording Trigger` | `str` |
730
+ | `device_mac` | `Device MAC` or `Device Mac` | `str` |
731
+ | `transfer_time` | `Call transfer time` | `datetime | None` |
732
+ | `transfer_related_call_id` | `Transfer related call ID` | `str` |
733
+ | `pstn_inbound` | derived | `bool` -- `True` if call type is `SIP_INBOUND` |
734
+ | `internal_call` | derived | `bool` -- `True` if call type is `SIP_ENTERPRISE` |
735
+ | `pstn_outbound` | derived | `bool` -- `True` if call type is `SIP NATIONAL` or `SIP_INTERNATIONAL` |
736
+
737
+ ---
738
+
739
+ ## 4. Reports
740
+
741
+ Report management lives under `org.reports`, which returns a `ReportList` instance.
742
+
743
+ ### ReportList (collection)
744
+
745
+ ```python
746
+ class ReportList(UserList):
747
+ def __init__(self, org: wxcadm.Org)
748
+ ```
749
+
750
+ Fetches existing reports on init from `GET /v1/reports`.
751
+
752
+ **Properties:**
753
+
754
+ ```python
755
+ @property
756
+ def templates(self) -> list[ReportTemplate]
757
+ ```
758
+
759
+ Lazy-loaded list of available report templates from `GET /v1/report/templates`.
760
+
761
+ **Methods:**
762
+
763
+ ```python
764
+ def get_template(self, id: int) -> Optional[ReportTemplate]
765
+ ```
766
+
767
+ Find a template by ID.
768
+
769
+ ```python
770
+ def list_reports(self) -> list[Report]
771
+ ```
772
+
773
+ Returns the current list of reports. Note: only shows API-created reports, not Control Hub reports.
774
+
775
+ ```python
776
+ def create_report(
777
+ self,
778
+ template: ReportTemplate,
779
+ start_date: Optional[str] = None,
780
+ end_date: Optional[str] = None,
781
+ site_list: Optional[list] = None
782
+ ) -> Report
783
+ ```
784
+
785
+ Creates a new report. Arguments depend on the template's `validations`.
786
+
787
+ ```python
788
+ def delete_report(self, report_id: str) -> bool
789
+ ```
790
+
791
+ Deletes a report. Returns `True` on success, `False` on exception.
792
+
793
+ ```python
794
+ def report_status(self, report_id: str) -> Optional[str]
795
+ ```
796
+
797
+ Returns the current status string of a report (triggers a refresh).
798
+
799
+ ```python
800
+ def get_report_lines(self, report_id: str) -> Optional[list]
801
+ ```
802
+
803
+ Downloads and returns the report lines as a list.
804
+
805
+ ```python
806
+ def cdr_report(
807
+ self,
808
+ start: Optional[str] = None, # YYYY-MM-DD
809
+ end: Optional[str] = None, # YYYY-MM-DD
810
+ days: Optional[int] = None
811
+ ) -> Report | bool
812
+ ```
813
+
814
+ Shortcut to create a "Calling Detailed Call History" report. Must provide either `start`/`end` or `days` (not both). When using `days`, the end date is set to yesterday. Returns the `Report` instance, or `False` if the CDR template is not found.
815
+
816
+ ### Report
817
+
818
+ ```python
819
+ class Report:
820
+ def __init__(
821
+ self,
822
+ org: wxcadm.Org,
823
+ id: Optional[str] = None,
824
+ config: Optional[dict] = None
825
+ )
826
+ ```
827
+
828
+ **Attributes:**
829
+
830
+ | Attribute | Type | Description |
831
+ |-----------|------|-------------|
832
+ | `id` | `str` | Report ID |
833
+ | `title` | `str` | Report title |
834
+ | `service` | `str` | Associated Webex service |
835
+ | `start_date` | `str` | Report period start |
836
+ | `end_date` | `str` | Report period end |
837
+ | `site_list` | `str` | Webex Meetings site list |
838
+ | `created` | `str` | Creation date |
839
+ | `created_by` | `str` | Creator ID |
840
+ | `scheduled_from` | `str` | `'API'` or `'Control Hub'` |
841
+ | `download_url` | `str | None` | URL to download the report |
842
+
843
+ **Properties:**
844
+
845
+ ```python
846
+ @property
847
+ def status(self) -> str
848
+ ```
849
+
850
+ Returns the current status. Automatically calls `refresh()` first.
851
+
852
+ **Methods:**
853
+
854
+ ```python
855
+ def refresh(self)
856
+ ```
857
+
858
+ Re-fetches report details from the API.
859
+
860
+ ```python
861
+ def get_report_lines(self) -> list
862
+ ```
863
+
864
+ Downloads the report. Handles both ZIP and plain-text content types. Returns lines as a list of strings. No further parsing is done -- the caller must split/clean lines.
865
+
866
+ ### ReportTemplate
867
+
868
+ ```python
869
+ class ReportTemplate:
870
+ def __init__(self, config: dict)
871
+ ```
872
+
873
+ | Attribute | Type | Description |
874
+ |-----------|------|-------------|
875
+ | `id` | `str` | Template ID |
876
+ | `title` | `str` | Template title |
877
+ | `service` | `str` | Webex service |
878
+ | `max_days` | `int` | Maximum days the report can span |
879
+ | `identifier` | `str` | Additional identifiers |
880
+ | `validations` | `list` | Validation rules for creating a report |
881
+
882
+ ---
883
+
884
+ ## 5. Jobs
885
+
886
+ Three async job types are supported, all following the same pattern: a job list class (fetches/creates jobs) and a job class (tracks status).
887
+
888
+ ### Number Management Jobs
889
+
890
+ For moving phone numbers between locations.
891
+
892
+ #### NumberManagementJobList
893
+
894
+ ```python
895
+ class NumberManagementJobList(UserList):
896
+ def __init__(self, org: wxcadm.Org)
897
+ ```
898
+
899
+ API endpoint: `v1/telephony/config/jobs/numbers/manageNumbers`
900
+
901
+ **Methods:**
902
+
903
+ ```python
904
+ def refresh(self) -> bool
905
+ ```
906
+
907
+ ```python
908
+ def get(self, id: str) -> Optional[NumberManagementJob]
909
+ ```
910
+
911
+ ```python
912
+ def create(
913
+ self,
914
+ job_type: str, # only "move" supported
915
+ target_location: Location | str,
916
+ numbers: list[str] # list of phone numbers to move
917
+ ) -> NumberManagementJob
918
+ ```
919
+
920
+ Creates a number move job. Validates numbers against `org.numbers` first. Raises `KeyError` if a number is not found in the org.
921
+
922
+ **Limitation:** The API currently supports only a single number per job, despite the list parameter.
923
+
924
+ #### NumberManagementJob
925
+
926
+ ```python
927
+ class NumberManagementJob:
928
+ def __init__(self, org: wxcadm.Org, id: str, details: Optional[dict] = None)
929
+ ```
930
+
931
+ **Properties:**
932
+
933
+ ```python
934
+ @property
935
+ def completed(self) -> bool
936
+ ```
937
+
938
+ Checks if `latestExecutionStatus == 'COMPLETED'`. Refreshes details on each call.
939
+
940
+ ```python
941
+ @property
942
+ def success(self) -> bool
943
+ ```
944
+
945
+ Checks counts: `True` if `numbersFailed == 0` and `numbersMoved + numbersDeleted == totalNumbers`. Should only be checked after `completed` is `True`.
946
+
947
+ ---
948
+
949
+ ### User Move Jobs
950
+
951
+ For moving users between locations.
952
+
953
+ #### UserMoveJobList
954
+
955
+ ```python
956
+ class UserMoveJobList(UserList):
957
+ def __init__(self, org: wxcadm.Org)
958
+ ```
959
+
960
+ API endpoint: `v1/telephony/config/jobs/person/moveLocation`
961
+
962
+ **Methods:**
963
+
964
+ ```python
965
+ def refresh(self) -> bool
966
+ ```
967
+
968
+ ```python
969
+ def get(self, id: str) -> Optional[UserMoveJob]
970
+ ```
971
+
972
+ ```python
973
+ def create(
974
+ self,
975
+ target_location: Location | str,
976
+ people: list[Person], # max 100
977
+ validate_only: Optional[bool] = False
978
+ ) -> tuple[UserMoveValidationResults, UserMoveJob | None]
979
+ ```
980
+
981
+ Two-phase process:
982
+ 1. **Validation** -- validates the first user in the list. If validation fails (`errors` present), the move is not attempted.
983
+ 2. **Move** -- if validation passes and `validate_only` is `False`, creates the actual move job.
984
+
985
+ Returns a tuple of `(UserMoveValidationResults, UserMoveJob | None)`. The job is `None` if validation failed or `validate_only` was `True`.
986
+
987
+ Raises `ValueError` if more than 100 people are provided.
988
+
989
+ #### UserMoveJob
990
+
991
+ ```python
992
+ class UserMoveJob:
993
+ def __init__(self, org: wxcadm.Org, id: str, details: Optional[dict] = None)
994
+ ```
995
+
996
+ **Properties:**
997
+
998
+ ```python
999
+ @property
1000
+ def completed(self) -> bool
1001
+ ```
1002
+
1003
+ Checks `latestExecutionStatus == 'COMPLETED'`.
1004
+
1005
+ ```python
1006
+ @property
1007
+ def success(self) -> bool
1008
+ ```
1009
+
1010
+ Checks `failed == 0` and `moved == totalMoves`.
1011
+
1012
+ #### UserMoveValidationResults
1013
+
1014
+ ```python
1015
+ class UserMoveValidationResults:
1016
+ def __init__(self)
1017
+ ```
1018
+
1019
+ **Attributes:**
1020
+ - `messages` (`list[dict]`) -- each dict has `severity` (`'error'` or `'impact'`), `code`, and `message`
1021
+
1022
+ **Properties:**
1023
+
1024
+ ```python
1025
+ @property
1026
+ def passed(self) -> bool
1027
+ ```
1028
+
1029
+ `True` if no messages have severity `'error'`.
1030
+
1031
+ **Methods:**
1032
+
1033
+ ```python
1034
+ def add_message(self, severity: str, code: Optional[str] = None, message: Optional[str] = None) -> bool
1035
+ ```
1036
+
1037
+ ---
1038
+
1039
+ ### Rebuild Phones Jobs
1040
+
1041
+ For triggering phone configuration rebuilds at a location.
1042
+
1043
+ #### RebuildPhonesJobList
1044
+
1045
+ ```python
1046
+ class RebuildPhonesJobList(UserList):
1047
+ def __init__(self, org: wxcadm.Org)
1048
+ ```
1049
+
1050
+ API endpoint: `v1/telephony/config/jobs/devices/rebuildPhones`
1051
+
1052
+ **Methods:**
1053
+
1054
+ ```python
1055
+ def refresh(self) -> bool
1056
+ ```
1057
+
1058
+ ```python
1059
+ def get(self, id: str) -> Optional[RebuildPhonesJob]
1060
+ ```
1061
+
1062
+ ```python
1063
+ def create(self, location: Location | str) -> RebuildPhonesJob
1064
+ ```
1065
+
1066
+ Creates a rebuild phones job for a given location. Only location-level rebuilds are supported.
1067
+
1068
+ #### RebuildPhonesJob
1069
+
1070
+ ```python
1071
+ class RebuildPhonesJob:
1072
+ def __init__(self, org: wxcadm.Org, id: str, details: Optional[dict] = None)
1073
+ ```
1074
+
1075
+ **Properties:**
1076
+
1077
+ ```python
1078
+ @property
1079
+ def completed(self) -> bool
1080
+ ```
1081
+
1082
+ ```python
1083
+ @property
1084
+ def device_count(self) -> int
1085
+ ```
1086
+
1087
+ Returns the number of devices included in the rebuild job.
1088
+
1089
+ `RebuildPhonesJob` does not have a `success` property unlike the other job types (`NumberManagementJob` and `UserMoveJob` both have one). The `completed` property's docstring incorrectly references a `success` attribute that was never implemented. To check success, inspect `self.details` dict directly after `completed` returns `True` (look for `deviceCount` and execution status fields).
1090
+
1091
+ ---
1092
+
1093
+ ## 6. Webhooks
1094
+
1095
+ Webhook management is accessed via `org.webhooks`, returning a `Webhooks` instance.
1096
+
1097
+ ### Webhooks (collection)
1098
+
1099
+ ```python
1100
+ class Webhooks(UserList):
1101
+ def __init__(self, org: wxcadm.Org)
1102
+ ```
1103
+
1104
+ Fetches all webhooks from `GET /v1/webhooks` and filters to the current org.
1105
+
1106
+ Note: Uses `self.org._parent.api` (the Webex-level API) rather than `self.org.api`, because webhooks are a platform-level resource.
1107
+
1108
+ **Properties:**
1109
+
1110
+ ```python
1111
+ @property
1112
+ def active(self) -> list[Webhook]
1113
+ ```
1114
+
1115
+ Returns only webhooks with status `'active'`.
1116
+
1117
+ ```python
1118
+ @property
1119
+ def inactive(self) -> list[Webhook]
1120
+ ```
1121
+
1122
+ Returns only webhooks with status `'inactive'`.
1123
+
1124
+ **Methods:**
1125
+
1126
+ ```python
1127
+ def add(
1128
+ self,
1129
+ name: str,
1130
+ url: str,
1131
+ resource: str,
1132
+ event: str,
1133
+ filter: str = None,
1134
+ secret: str = None,
1135
+ owner: str = None
1136
+ ) -> bool
1137
+ ```
1138
+
1139
+ Creates a new webhook. See [Webex webhook docs](https://developer.webex.com/docs/webhooks) for valid `resource`, `event`, and `filter` values. Returns `True` on success, `False` otherwise.
1140
+
1141
+ ### Webhook (dataclass)
1142
+
1143
+ ```python
1144
+ @dataclass
1145
+ class Webhook:
1146
+ api: wxcadm.WebexApi
1147
+ orgId: str
1148
+ appId: str
1149
+ id: str
1150
+ name: str
1151
+ targetUrl: str
1152
+ resource: str
1153
+ event: str
1154
+ status: str # 'active' or 'inactive'
1155
+ created: str
1156
+ createdBy: str
1157
+ ownedBy: str
1158
+ filter: Optional[str] = None
1159
+ secret: Optional[str] = None
1160
+ ```
1161
+
1162
+ **Methods:**
1163
+
1164
+ All update methods send a full `PUT` to `v1/webhooks/{id}` with all fields (the API requires a complete payload on update).
1165
+
1166
+ ```python
1167
+ def delete(self) -> bool
1168
+ ```
1169
+
1170
+ Deletes the webhook.
1171
+
1172
+ ```python
1173
+ def change_url(self, url: str) -> bool
1174
+ ```
1175
+
1176
+ Changes the target URL.
1177
+
1178
+ ```python
1179
+ def change_name(self, name: str) -> bool
1180
+ ```
1181
+
1182
+ Changes the webhook name.
1183
+
1184
+ ```python
1185
+ def change_secret(self, secret: str) -> bool
1186
+ ```
1187
+
1188
+ Changes the secret value for signature generation.
1189
+
1190
+ ```python
1191
+ def change_owner(self, owner: str) -> bool
1192
+ ```
1193
+
1194
+ Changes the webhook owner.
1195
+
1196
+ ```python
1197
+ def deactivate(self) -> bool
1198
+ ```
1199
+
1200
+ Sets status to `'inactive'`. Returns `True` even if already inactive.
1201
+
1202
+ ```python
1203
+ def activate(self) -> bool
1204
+ ```
1205
+
1206
+ Sets status to `'active'`. Returns `True` even if already active.
1207
+
1208
+ ---
1209
+
1210
+ ## 7. wxcadm vs wxc_sdk Comparison
1211
+
1212
+ | Feature Area | wxcadm | wxc_sdk |
1213
+ |-------------|--------|---------|
1214
+ | **Trunks** | `org.call_routing.trunks` -- full CRUD, lazy detail loading | `api.telephony.prem_pstn.trunk` -- similar CRUD via Webex API |
1215
+ | **Route Groups** | `org.call_routing.route_groups` -- list, get, add trunk to group | `api.telephony.prem_pstn.route_group` -- full CRUD |
1216
+ | **Route Lists** | `org.call_routing.route_lists` -- list, get, create (no delete) | `api.telephony.prem_pstn.route_list` -- full CRUD including delete |
1217
+ | **Dial Plans** | `org.call_routing.dial_plans` -- list, pattern add/delete (no collection `get()`) | `api.telephony.prem_pstn.dial_plan` -- full CRUD |
1218
+ | **Translation Patterns** | `TranslationPatternList` -- org or location scope, full CRUD | `api.telephony.call_routing.tp` -- similar scope and CRUD |
1219
+ | **Call Routing Test** | `org.call_routing.test()` | `api.telephony.test_call_routing()` |
1220
+ | **PSTN** | `LocationPSTN` -- get/set provider per location | Covered under location telephony settings |
1221
+ | **CDR** | `CallDetailRecords` -- post-processing class, records passed in externally | No direct equivalent; CDRs obtained via reports or analytics API |
1222
+ | **Reports** | `ReportList` -- template-based report creation with `cdr_report()` shortcut | `api.reports` -- similar template-based approach |
1223
+ | **Jobs (Number Move)** | `NumberManagementJobList` -- move only, single number limit | `api.telephony.jobs.manage_numbers` -- similar |
1224
+ | **Jobs (User Move)** | `UserMoveJobList` -- validate-then-move pattern, max 100 users | `api.telephony.jobs.move_users` -- similar |
1225
+ | **Jobs (Rebuild Phones)** | `RebuildPhonesJobList` -- location-level only | `api.telephony.jobs.rebuild_phones` -- similar |
1226
+ | **Webhooks** | `Webhooks` -- full CRUD with activate/deactivate | `api.webhook` -- full CRUD |
1227
+
1228
+ **Key architectural differences:**
1229
+
1230
+ - **wxcadm** uses `UserList` subclasses throughout, so collections are iterable lists that also have `.get()` methods. Properties often trigger API calls on access (lazy loading).
1231
+ - **wxc_sdk** uses explicit list/get/create/update/delete methods on API wrapper classes. Data is returned as typed dataclass models.
1232
+ - **wxcadm CDR processing** is unique -- it builds a `Call -> CallLeg -> LegPart` hierarchy from raw CSV records with automatic transfer merging. wxc_sdk does not have an equivalent post-processing layer.
1233
+ - **wxcadm job validation** (particularly `UserMoveJobList.create`) performs a two-phase validate-then-execute pattern in a single method call, returning both validation results and the job. wxc_sdk typically separates these concerns.
1234
+
1235
+ ---
1236
+
1237
+ ## API Endpoint Summary
1238
+
1239
+ | Module | Endpoint Pattern |
1240
+ |--------|-----------------|
1241
+ | Trunks | `v1/telephony/config/premisePstn/trunks` |
1242
+ | Route Groups | `v1/telephony/config/premisePstn/routeGroups` |
1243
+ | Route Lists | `v1/telephony/config/premisePstn/routeLists` |
1244
+ | Dial Plans | `v1/telephony/config/premisePstn/dialPlans` |
1245
+ | Translation Patterns | `v1/telephony/config/callRouting/translationPatterns` |
1246
+ | Translation Patterns (location) | `v1/telephony/config/locations/{id}/callRouting/translationPatterns` |
1247
+ | Call Routing Test | `v1/telephony/config/actions/testCallRouting/invoke` |
1248
+ | PSTN Connection | `v1/telephony/pstn/locations/{id}/connection` |
1249
+ | PSTN Connection Options | `v1/telephony/pstn/locations/{id}/connectionOptions` |
1250
+ | Reports | `v1/reports` |
1251
+ | Report Templates | `v1/report/templates` |
1252
+ | Number Move Jobs | `v1/telephony/config/jobs/numbers/manageNumbers` |
1253
+ | User Move Jobs | `v1/telephony/config/jobs/person/moveLocation` |
1254
+ | Rebuild Phones Jobs | `v1/telephony/config/jobs/devices/rebuildPhones` |
1255
+ | Webhooks | `v1/webhooks` |
1256
+
1257
+ ---
1258
+
1259
+ ## See Also
1260
+
1261
+ - [call-routing.md](call-routing.md) — wxc_sdk call routing APIs (trunks, route groups, route lists, dial plans, translation patterns)
1262
+ - [reporting-analytics.md](reporting-analytics.md) — wxc_sdk reporting and CDR access patterns for comparison with wxcadm's CDR post-processing