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,1318 @@
1
+ <!-- Updated by playbook session 2026-03-18 -->
2
+
3
+ # Reporting & Analytics
4
+
5
+ CDR feed, report templates, report generation/download, and call quality/queue/AA statistics for Webex Calling.
6
+
7
+ ## Sources
8
+
9
+ - `wxc_sdk.cdr` — SDK source for Detailed CDR API (`../wxc_sdk_reference/wxc_sdk/cdr/__init__.py`)
10
+ - `wxc_sdk.reports` — SDK source for Reports API (`../wxc_sdk_reference/wxc_sdk/reports/__init__.py`)
11
+ - [Exploring the Webex Calling Reports and Analytics APIs](https://developer.webex.com/blog/exploring-the-webex-calling-reports-and-analytics-apis)
12
+ - [Webex CDR field reference](https://help.webex.com/en-us/article/nmug598/Reports-for-Your-Cloud-Collaboration-Portfolio)
13
+
14
+ ---
15
+
16
+ ## Required Scopes
17
+
18
+ | Scope | Purpose |
19
+ |-------|---------|
20
+ | `spark-admin:calling_cdr_read` | Access Detailed Call History (CDR feed/stream). Authenticating user must also have the "Webex Calling Detailed Call History API access" admin role enabled in Control Hub. |
21
+ | `analytics:read_all` | Access Report Templates and Reports APIs. Authenticating user must be a read-only or full administrator. |
22
+ | `spark-admin:locations_read` | Filter CDR queries by location name. |
23
+
24
+ **License requirement:** The Reports API requires the **Pro Pack for Cisco Webex** license on the organization.
25
+
26
+ ---
27
+
28
+ ## 1. Detailed Call History (CDR) API
29
+
30
+ ### Endpoint
31
+
32
+ | Property | Value |
33
+ |----------|-------|
34
+ | Base URL (commercial) | `https://analytics-calling.webexapis.com` |
35
+ | Base URL (government) | `https://analytics-calling-gov.webexapis.com` |
36
+ | CDR Feed path | `/v1/cdr_feed` |
37
+ | CDR Stream path | `/v1/cdr_stream` |
38
+ | Method | `GET` |
39
+
40
+ - **CDR Feed** (`cdr_feed`) — Pull records for a specific time window based on **call start time**. Best for batch/historical pulls. 12-hour max window, 30-day retention.
41
+ - **CDR Stream** (`cdr_stream`) — Returns records based on their **database write time** (when the record was ingested into the Webex Calling cloud), not when the call occurred. 2-hour max window, 12-hour retention. Best for near-real-time monitoring — catches late-arriving records that Feed may miss.
42
+
43
+ **Late-arriving records:** A call placed at 11:59 lasting 10 minutes generates a CDR record around 12:10. CDR Feed querying 11:00–12:00 misses this call (it started in-window but the record didn't exist yet). CDR Stream querying 12:00–13:00 catches it because it queries by write time, not call time.
44
+
45
+ If the region's servers do not host the organization's data, an **HTTP 451** is returned. The response body contains the correct regional endpoint to use instead.
46
+
47
+ ### Query Parameters
48
+
49
+ | Parameter | Type | Required | Description |
50
+ |-----------|------|----------|-------------|
51
+ | `startTime` | ISO 8601 datetime | Yes | Start of time window. Feed: call start time. Stream: database write time. |
52
+ | `endTime` | ISO 8601 datetime | Yes | End of time window. Must be after `startTime`. |
53
+ | `locations` | comma-separated strings | No | Location names (as shown in Control Hub). Up to 10. |
54
+ | `max` | int | No | Results per page (pagination). |
55
+
56
+ ### Time Range Constraints
57
+
58
+ | Constraint | CDR Feed | CDR Stream |
59
+ |------------|----------|------------|
60
+ | **Timestamp meaning** | Call start time | Database write time |
61
+ | **Max window per request** | 12 hours | 2 hours |
62
+ | **Data retention** | 30 days | 12 hours |
63
+ | **Oldest data** | 30 days ago | 12 hours ago |
64
+ | **Newest data** | ~5 minutes ago | ~1 minute ago |
65
+
66
+ - **Data generation lag:** CDR records may take up to 24 hours after call completion to fully populate. Most appear within 5 minutes.
67
+ - **Late records:** CDR Stream automatically captures late-arriving records via write-time semantics. CDR Feed may miss calls whose records are generated after the queried window closes.
68
+
69
+ ### Rate Limits
70
+
71
+ - **1 initial request per minute** per user token.
72
+ - **Up to 10 additional pagination requests per minute** per user token.
73
+ - A 7-day historical pull via CDR Feed requires 14 sequential requests = ~14 minutes. For windows >7 days, consider the Reports API (Detailed Call History template) instead.
74
+
75
+ ### Pagination
76
+
77
+ The API returns paginated results via the standard `items` key. The SDK method `session.follow_pagination()` handles automatic page traversal.
78
+
79
+ ### SDK Method
80
+
81
+ ```python
82
+ DetailedCDRApi.get_cdr_history(
83
+ start_time: Union[str, datetime] = None, # Default: ~48 hours ago
84
+ end_time: Union[str, datetime] = None, # Default: ~5 minutes ago
85
+ locations: list[str] = None, # Up to 10 location names
86
+ host: str = 'analytics-calling.webexapis.com',
87
+ stream: bool = False, # True = use cdr_stream instead of cdr_feed
88
+ **params
89
+ ) -> Generator[CDR, None, None]
90
+ ```
91
+
92
+ When `start_time` and `end_time` are omitted, the SDK defaults to a window of approximately 48 hours ago through 5 minutes ago.
93
+
94
+ **Usage example:**
95
+
96
+ ```python
97
+ from wxc_sdk import WebexSimpleApi
98
+
99
+ api = WebexSimpleApi(tokens='<access_token>')
100
+
101
+ # Pull last 2 hours of CDRs
102
+ from datetime import datetime, timedelta, timezone
103
+ end = datetime.now(timezone.utc) - timedelta(minutes=5)
104
+ start = end - timedelta(hours=2)
105
+
106
+ for cdr in api.cdr.get_cdr_history(start_time=start, end_time=end):
107
+ print(f"{cdr.start_time} | {cdr.direction} | {cdr.calling_number} -> {cdr.called_number} | {cdr.duration}s")
108
+ ```
109
+
110
+ ### CLI Examples
111
+
112
+ ```bash
113
+ # Pull CDR feed for a 2-hour window (batch/historical)
114
+ wxcli cdr list \
115
+ --start-time "2026-03-17T14:00:00.000Z" \
116
+ --end-time "2026-03-17T16:00:00.000Z"
117
+
118
+ # Pull CDR feed filtered by location
119
+ wxcli cdr list \
120
+ --start-time "2026-03-17T14:00:00.000Z" \
121
+ --end-time "2026-03-17T16:00:00.000Z" \
122
+ --locations "San Jose,Austin"
123
+
124
+ # Pull CDR feed with JSON output for scripting
125
+ wxcli cdr list \
126
+ --start-time "2026-03-17T14:00:00.000Z" \
127
+ --end-time "2026-03-17T16:00:00.000Z" \
128
+ -o json
129
+
130
+ # Pull CDR feed with pagination control
131
+ wxcli cdr list \
132
+ --start-time "2026-03-17T14:00:00.000Z" \
133
+ --end-time "2026-03-17T16:00:00.000Z" \
134
+ --max 500 --limit 100
135
+
136
+ # Pull near-real-time CDR stream
137
+ wxcli cdr list-cdr_stream \
138
+ --start-time "2026-03-17T14:00:00.000Z" \
139
+ --end-time "2026-03-17T16:00:00.000Z"
140
+
141
+ # CDR stream filtered by location with JSON output
142
+ wxcli cdr list-cdr_stream \
143
+ --start-time "2026-03-17T14:00:00.000Z" \
144
+ --end-time "2026-03-17T16:00:00.000Z" \
145
+ --locations "Denver" -o json
146
+ ```
147
+
148
+ ### CDR Record Fields
149
+
150
+ The `CDR` model (class `wxc_sdk.cdr.CDR`) contains 55+ fields. Field names arrive from the API in space-separated format (e.g., "Answer time") and are normalized to snake_case by the SDK.
151
+
152
+ #### Core Call Fields
153
+
154
+ | SDK Field | Type | Description |
155
+ |-----------|------|-------------|
156
+ | `start_time` | `datetime` | Call start time (UTC). Answer time may be slightly after. |
157
+ | `answer_time` | `datetime` | Time the call was answered (UTC). |
158
+ | `release_time` | `datetime` | Time the call finished (UTC). |
159
+ | `duration` | `int` | Call length in seconds. |
160
+ | `ring_duration` | `int` | Ringing time before answer/timeout, in seconds. |
161
+ | `hold_duration` | `int` | Total hold time in seconds (floor value). |
162
+ | `answered` | `bool` | Whether this call leg was answered. |
163
+ | `answer_indicator` | `str` | `Yes`, `No`, or `Yes-PostRedirection`. |
164
+ | `direction` | `CDRDirection` | `ORIGINATING` or `TERMINATING`. |
165
+ | `call_type` | `CDRCallType` | See enum values below. |
166
+ | `call_outcome` | `str` | `Success`, `Failure`, or `Refusal`. |
167
+ | `call_outcome_reason` | `str` | Detailed reason (e.g., `Normal`, `UserBusy`, `NoAnswer`, `CallRejected`, `UnassignedNumber`, `SIP408`, `AdminCallBlock`, etc.). |
168
+ | `releasing_party` | `str` | `Local`, `Remote`, or `Unknown`. |
169
+
170
+ #### Number / Identity Fields
171
+
172
+ | SDK Field | Type | Description |
173
+ |-----------|------|-------------|
174
+ | `calling_number` | `str` | Incoming: calling party number. Outgoing: user's number. |
175
+ | `called_number` | `str` | Incoming: user's number. Outgoing: called party number. |
176
+ | `calling_line_id` | `str` | Incoming: calling party line ID. Outgoing: user's line ID. |
177
+ | `called_line_id` | `str` | Incoming: user's line ID. Outgoing: called party line ID. |
178
+ | `dialed_digits` | `str` | Raw keypad digits before pre-translations. Originating calls only. |
179
+ | `user` | `str` | The user who made or received the call. |
180
+ | `user_number` | `str` | User's E.164 number (or extension if no number assigned). |
181
+ | `user_type` | `CDRUserType` | See enum values below. |
182
+ | `user_uuid` | `str` | Unique user identifier across Cisco products. |
183
+ | `caller_id_number` | `str` | Presentation number based on caller ID settings in Control Hub. |
184
+ | `external_caller_id_number` | `str` | Set when external caller ID is location number or org number (not direct line). |
185
+ | `redirecting_number` | `str` | Last redirecting number (for transferred/forwarded calls). |
186
+ | `redirecting_party_uuid` | `str` | UUID of the last redirecting party. |
187
+ | `original_called_party_uuid` | `str` | UUID of the first redirecting party (alias: `Original Called Party UUID`). |
188
+
189
+ #### Session / Correlation IDs
190
+
191
+ | SDK Field | Type | Description |
192
+ |-----------|------|-------------|
193
+ | `call_id` | `str` | SIP Call ID. Share with Cisco TAC for troubleshooting. |
194
+ | `correlation_id` | `str` | Ties together multiple call legs of the same session. |
195
+ | `local_session_id` | `str` | UUID from originating user agent (alias: `local_sessionid`). |
196
+ | `remote_session_id` | `str` | UUID from terminating user agent (alias: `remote_sessionid`). |
197
+ | `final_local_session_id` | `str` | Local Session ID value at call end (alias: `final_local_sessionid`). |
198
+ | `final_remote_session_id` | `str` | Remote Session ID value at call end (alias: `final_remote_sessionid`). |
199
+ | `local_call_id` | `str` | Correlation identity for this CDR. Used with `remote_call_id` to identify the paired CDR of the same call leg: if A.local_call_id = B.remote_call_id AND A.remote_call_id = B.local_call_id, A and B are the two CDRs of the same leg. |
200
+ | `remote_call_id` | `str` | The `local_call_id` value of the other CDR in this call leg. If A.local_call_id = B.remote_call_id, A and B form a leg pair. |
201
+ | `network_call_id` | `str` | Secondary leg-pairing key. If A.network_call_id = B.network_call_id, A and B are the two CDRs of the same call leg. Use this when the local/remote pair is not present (cloud distributes processing internally). |
202
+ | `related_call_id` | `str` | Deflection chaining key. In a deflection (HG ring, forward, redirect), the new leg's ORIGINATING CDR has `related_call_id` = the source leg's TERMINATING CDR's `local_call_id`. This chains each delivery attempt back to the HG/forwarding party. Also used for service activation (recording, executive-assistant) where a new call is spawned. |
203
+ | `transfer_related_call_id` | `str` | Contains the `local_call_id` of a CDR in the **other** call group involved in an attended (consultative) transfer. Linking rule: if CDR1.transfer_related_call_id = CDR2.local_call_id, then CDR1.correlation_id and CDR2.correlation_id belong to the same transfer interaction. Not populated for blind transfers. |
204
+ | `interaction_id` | `str` | Correlates CDRs linked by service interaction (e.g., consult + transfer). |
205
+
206
+ ### CDR Data Model — Call Legs and ID Relationships
207
+
208
+ Every call generates one CDR record per **call leg**. A call leg is one participant's perspective of the call. Understanding how legs relate is critical for accurate reporting.
209
+
210
+ #### How legs are generated
211
+
212
+ | Scenario | Legs Generated | Correlation ID |
213
+ |----------|---------------|----------------|
214
+ | **Simple point-to-point call** | 2 legs: ORIGINATING (caller) + TERMINATING (callee) | Same Correlation ID on both legs |
215
+ | **Blind transfer** | Original call: 2 legs. New call to transfer target: 2 new legs with a **new Correlation ID**. `Transfer related call ID` is **not populated**. | Different Correlation ID per call |
216
+ | **Attended (consultative) transfer** | Original call: 2 legs. Consultation call: 2 legs with a new Correlation ID. After transfer completes, the consultation leg's `Transfer related call ID` contains the **Correlation ID of the original call**. | Different Correlation ID per call, linked by `Transfer related call ID` |
217
+ | **Hunt Group / Call Queue** | Inbound call + each HG ring attempt share the **same Correlation ID**. HG itself appears in CDRs as: (1) TERMINATING — the HG "receives" the inbound call; (2) ORIGINATING — the HG "dials" each agent delivery attempt. Each agent attempt produces a HG ORIGINATING CDR + an agent TERMINATING CDR, linked by `related_call_id` = HG's TERMINATING CDR's `local_call_id`. | Same Correlation ID throughout |
218
+ | **Call forwarding (CFA/CFB/CFNA)** | Original call legs + forwarded legs share the same Correlation ID. `Redirect reason` and `Original reason` explain why it was forwarded. | Same Correlation ID |
219
+ | **Call park + retrieve** | Park and retrieve events share the same Correlation ID. `Related reason` = `CallPark` or `CallParkRetrieve`. | Same Correlation ID |
220
+
221
+ #### Key ID relationships
222
+
223
+ ```
224
+ Correlation ID ─── groups all legs of ONE call (or one HG/CQ interaction)
225
+ │ A single call involving a transfer produces 2+ Correlation IDs
226
+ │ linked by transfer_related_call_id
227
+
228
+ ├── local_call_id / remote_call_id ─── identify the two CDRs of one call leg
229
+ │ if A.local_call_id = B.remote_call_id → A and B are the same leg
230
+
231
+ ├── network_call_id ─── secondary leg-pairing key (use when local/remote pair absent)
232
+ │ if A.network_call_id = B.network_call_id → A and B are the same leg
233
+
234
+ ├── related_call_id ─── deflection chaining key
235
+ │ ORIGINATING CDR's related_call_id = TERMINATING CDR's local_call_id
236
+ │ Links each HG ring attempt / forward leg back to the source TERMINATING CDR
237
+ │ Also used when a new call is spawned by service activation (recording, exec-assistant)
238
+
239
+ ├── transfer_related_call_id ─── contains a local_call_id from the OTHER call group
240
+ │ Linking rule: CDR1.transfer_related_call_id = CDR2.local_call_id
241
+ │ → CDR1.correlation_id is linked to CDR2.correlation_id
242
+ │ Present in attended (consultative) transfers; NOT populated for blind transfers
243
+
244
+ └── interaction_id ─── correlates CDRs linked by service interaction
245
+ (consult + transfer sequence)
246
+ ```
247
+
248
+ #### Transfer Correlation ID linking rule
249
+
250
+ Two Correlation IDs belong to the same transfer interaction when:
251
+
252
+ ```
253
+ CDR1.'transfer_related_call_id' = CDR2.'local_call_id'
254
+ ```
255
+
256
+ Then CDR1.correlation_id and CDR2.correlation_id are linked. Search for the ultimate destination and CDR correlation must be done across all CDRs sharing any of the linked Correlation IDs. Optionally limit the search to CDR pairs that overlap in time.
257
+
258
+ #### Direction semantics per leg
259
+
260
+ - **ORIGINATING**: The leg from the perspective of the party who **placed** the call (or initiated the delivery/forward).
261
+ - **TERMINATING**: The leg from the perspective of the party who **received** the call (or was the target of the delivery).
262
+ - A single call always produces at least one ORIGINATING and one TERMINATING leg.
263
+ - **Hunt Group CDR pattern**: HG appears in CDRs with `user_type = HuntGroup` in **both** directions:
264
+ - HG TERMINATING — the HG "receives" the inbound call (one per Correlation ID)
265
+ - HG ORIGINATING — the HG "dials" each agent delivery attempt (one per ring attempt)
266
+ - Agent TERMINATING — the agent who receives the ring (one per ring attempt, paired with the HG ORIGINATING above)
267
+ - A HG call with 3 ring attempts produces: 1 inbound ORIGINATING + 1 HG TERMINATING + 3×(HG ORIGINATING + agent TERMINATING) = 8 CDRs total.
268
+ - `Answered=true` on a HuntGroup-type CDR means the HG processed/accepted the call — **not** that a human answered it.
269
+
270
+ #### Answer indicator per leg
271
+
272
+ - `Yes` — this specific leg was answered.
273
+ - `No` — this leg was not answered (rang out, rejected, or redirected before answer).
274
+ - `Yes-PostRedirection` — this leg was answered, but only after being redirected (forwarded/transferred) from another destination.
275
+ - In a hunt group, the HG TERMINATING CDR has `Yes` (HG accepted the call). For agent delivery: only the HG ORIGINATING + agent TERMINATING pair for the answering attempt have `Yes`; all other agent attempts have `No`.
276
+
277
+ #### Ultimate destination rule
278
+
279
+ To find the CDR representing the ultimate answering party:
280
+
281
+ **Simple rule:** `answered=Yes`, `direction=TERMINATING`, and either `user_type=LocalGateway` (on-prem answer) OR the CDR is the only answered TERMINATING CDR for that Correlation ID group.
282
+
283
+ **Robust rule:** `answered=Yes`, `direction=TERMINATING`, AND `local_call_id` is **not** referenced by any `related_call_id` or `transfer_related_call_id` across all linked CDRs. (Handles edge cases where a user forwards or transfers a call that comes back to them.)
284
+
285
+ #### Webhook delivery and deduplication
286
+
287
+ CDR webhooks deliver records every 5 minutes with automatic late-record backfill. Duplicate records may appear in subsequent payloads. Use the `report_id` field as the primary dedup key and `report_time` to determine record freshness.
288
+
289
+ #### Routing / Redirect Fields
290
+
291
+ | SDK Field | Type | Description |
292
+ |-----------|------|-------------|
293
+ | `original_reason` | `CDROriginalReason` | Why call was originally redirected. |
294
+ | `redirect_reason` | `CDRRedirectReason` | Why call was redirected at this step. |
295
+ | `related_reason` | `CDRRelatedReason` | Service-level reason (transfer, forward, park, pickup, etc.). |
296
+ | `route_group` | `str` | Route group for outbound calls via premises PSTN. Originating records only. |
297
+ | `route_list_calls_overage` | `str` | Number of bursting calls over licensed volume (alias: `Route list calls overage`). |
298
+
299
+ #### Client / Device Fields
300
+
301
+ | SDK Field | Type | Description |
302
+ |-----------|------|-------------|
303
+ | `client_type` | `CDRClientType` | See enum values below. |
304
+ | `client_version` | `str` | Client app version. |
305
+ | `os_type` | `str` | Operating system of the app. |
306
+ | `model` | `str` | Device model type. |
307
+ | `device_mac` | `str` | MAC address of the device. |
308
+ | `sub_client_type` | `str` | `MOBILE_NETWORK` for Webex Go calls from mobile. |
309
+ | `device_owner_uuid` | `str` | UUID of device owner (for multi-line/shared line). |
310
+
311
+ #### Location / Organization Fields
312
+
313
+ | SDK Field | Type | Description |
314
+ |-----------|------|-------------|
315
+ | `location` | `str` | Location name for the report. |
316
+ | `site_main_number` | `str` | Main number for the user's site. |
317
+ | `site_timezone` | `str` | Offset in minutes from UTC for user's timezone. |
318
+ | `site_uuid` | `str` | Unique site identifier. |
319
+ | `org_uuid` | `str` | Unique organization identifier across Cisco. |
320
+ | `department_id` | `str` | User's department name identifier. |
321
+ | `authorization_code` | `str` | Auth code for Account/Authorization Codes service. |
322
+ | `international_country` | `str` | Country code of dialed number (international calls only). |
323
+
324
+ #### Trunk Fields
325
+
326
+ | SDK Field | Type | Description |
327
+ |-----------|------|-------------|
328
+ | `inbound_trunk` | `str` | Inbound trunk (present in both Originating and Terminating records). |
329
+ | `outbound_trunk` | `str` | Outbound trunk (present in both Originating and Terminating records). |
330
+
331
+ #### PSTN Vendor Fields
332
+
333
+ | SDK Field | Type | Description |
334
+ |-----------|------|-------------|
335
+ | `pstn_vendor_name` | `str` | PSTN service vendor name for the country. |
336
+ | `pstn_legal_entity` | `str` | Regulated business entity for PSTN. Cisco Calling Plans only. |
337
+ | `pstn_vendor_org_id` | `str` | Cisco Calling plan org UUID (unique across regions). |
338
+ | `pstn_provider_id` | `str` | Immutable Cisco-defined UUID for the PSTN provider. |
339
+ | `external_customer_id` | `str` | External customer identifier. |
340
+
341
+ #### Recording Fields
342
+
343
+ | SDK Field | Type | Description |
344
+ |-----------|------|-------------|
345
+ | `call_recording_platform_name` | `str` | Platform name: `DubberRecorder`, `Webex`, `Eleveo`, `ASCTech`, `MiaRec`, `Imagicle`, or `Unknown`. |
346
+ | `call_recording_result` | `str` | `successful`, `failed`, or `successful but not kept`. |
347
+ | `call_recording_trigger` | `str` | `always`, `always-pause-resume`, `on-demand`, or `on-demand-user-start`. |
348
+
349
+ #### Call Queue / Auto-Attendant Fields
350
+
351
+ | SDK Field | Type | Description |
352
+ |-----------|------|-------------|
353
+ | `queue_type` | `str` | `Customer Assist` or `Call Queue` (alias: `Queue Type`). |
354
+ | `auto_attendant_key_pressed` | `str` | Last DTMF key pressed by caller (alias: `Auto Attendant Key Pressed`). |
355
+
356
+ #### Other Fields
357
+
358
+ | SDK Field | Type | Description |
359
+ |-----------|------|-------------|
360
+ | `report_id` | `str` | Unique ID for deduplication. |
361
+ | `report_time` | `datetime` | Time this CDR record was created (UTC). |
362
+ | `call_transfer_time` | `datetime` | Time the transfer service was invoked (UTC). |
363
+ | `recall_type` | `str` | Indicates call park recall (alias: `Recall Type`). |
364
+ | `answered_elsewhere` | `str` | `Yes` when another agent/user answered (e.g., hunt group simultaneous ring). |
365
+ | `public_calling_ip_address` | `str` | Public IP of calling device (India locations only). |
366
+ | `public_called_ip_address` | `str` | Public IP of called device (India locations only). |
367
+ | `wx_cc_consult_merge_status` | `str` | WxCC consult transfer/conference status: `Yes`, `No`, or `NA`. |
368
+
369
+ #### Caller Reputation Fields
370
+
371
+ | SDK Field | Type | Description |
372
+ |-----------|------|-------------|
373
+ | `caller_reputation_score` | `str` | Score from caller reputation provider (0.0 to 5.0). |
374
+ | `caller_reputation_service_result` | `str` | `allow`, `captcha-allow`, `captcha-block`, or `block`. Terminating CDRs only. |
375
+ | `caller_reputation_score_reason` | `str` | Reason for the reputation score, or error details. |
376
+
377
+ ### CDR Enums
378
+
379
+ #### CDRCallType
380
+
381
+ | Value | Meaning |
382
+ |-------|---------|
383
+ | `SIP_MEETING` | Meeting call |
384
+ | `SIP_INTERNATIONAL` | International call |
385
+ | `SIP_SHORTCODE` | Short code call |
386
+ | `SIP_INBOUND` | Inbound SIP call |
387
+ | `SIP_EMERGENCY` | Emergency call |
388
+ | `SIP_PREMIUM` | Premium rate call |
389
+ | `SIP_ENTERPRISE` | Enterprise (on-net) call |
390
+ | `SIP_TOLLFREE` | Toll-free call |
391
+ | `SIP_NATIONAL` | National call |
392
+ | `SIP_MOBILE` | Mobile call |
393
+ | `SIP_URI` | SIP URI call |
394
+ | `SIP_OPERATOR` | Operator-assisted call |
395
+ | `SIP_CLICKTOCALL` | Click-to-call initiated |
396
+ | `ZTN` | Zero-touch notification |
397
+ | `UNKNOWN` | Unknown call type |
398
+
399
+ #### CDRClientType
400
+
401
+ | Value | Meaning |
402
+ |-------|---------|
403
+ | `SIP` | SIP device/endpoint |
404
+ | `WXC_CLIENT` | Webex Calling native client |
405
+ | `WXC_THIRD_PARTY` | Third-party client |
406
+ | `TEAMS_WXC_CLIENT` | Teams with Webex Calling client |
407
+ | `TEAMS_CLIENT` | Microsoft Teams client |
408
+ | `TEAMS_DEVICE` | Microsoft Teams device |
409
+ | `TEAMS_SHARE` | Microsoft Teams shared device |
410
+ | `CLOUD_AWARE_SIP` | Cloud-aware SIP endpoint |
411
+ | `WXC_DEVICE` | Webex Calling device |
412
+ | `WXC_SIP_GW` | Webex Calling SIP gateway |
413
+
414
+ #### CDRDirection
415
+
416
+ | Value | Meaning |
417
+ |-------|---------|
418
+ | `ORIGINATING` | Outbound call |
419
+ | `TERMINATING` | Inbound call |
420
+
421
+ #### CDROriginalReason
422
+
423
+ Values: `Unconditional`, `NoAnswer`, `CallQueue`, `HuntGroup`, `TimeOfDay`, `UserBusy`, `FollowMe`, `Unrecognised`, `Deflection`, `Unavailable`, `Unknown`
424
+
425
+ #### CDRRedirectReason
426
+
427
+ Values: `Unconditional`, `NoAnswer`, `CallQueue`, `TimeOfDay`, `UserBusy`, `FollowMe`, `HuntGroup`, `Deflection`, `Unknown`, `Unavailable`
428
+
429
+ #### CDRRelatedReason
430
+
431
+ Values: `ConsultativeTransfer`, `CallForwardSelective`, `CallForwardAlways`, `CallForwardNoAnswer`, `CallForwardBusy`, `CallForwardNotReachable`, `CallForwardModeBased`, `CallPark`, `CallParkRetrieve`, `CallQueue`, `CallPickup`, `DirectedCallPickup`, `CallRetrieve`, `CallRecording`, `Deflection`, `FaxDeposit`, `HuntGroup`, `PushNotificationRetrieval`, `VoiceXMLScriptTermination`, `AnywhereLocation`, `AnywherePortal`, `Executive`, `ExecutiveAssistantInitiateCall`, `ExecutiveAssistantDivert`, `ExecutiveAssistantCallPush`, `ExecutiveForward`, `RemoteOffice`, `RoutePoint`, `SequentialRing`, `SimultaneousRingPersonal`, `CCMonitoringBI`, `BargeIn`, `Unrecognised`
432
+
433
+ #### CDRUserType
434
+
435
+ Values: `AutomatedAttendantVideo`, `Anchor`, `BroadworksAnywhere`, `VoiceMailRetrieval`, `LocalGateway`, `HuntGroup`, `GroupPaging`, `User`, `VoiceMailGroup`, `CallCenterStandard`, `CallCenterPremium`, `VoiceXML`, `RoutePoint`, `VirtualLine`, `Place`, `RouteList`
436
+
437
+ ---
438
+
439
+ ## 2. Report Templates API
440
+
441
+ Report templates define the available report types. You must list templates to get the `templateId` needed to create a report.
442
+
443
+ ### SDK Method
444
+
445
+ ```python
446
+ ReportsApi.list_templates() -> list[ReportTemplate]
447
+ ```
448
+
449
+ **REST equivalent:** `GET https://webexapis.com/v1/report/templates`
450
+
451
+ ### ReportTemplate Model
452
+
453
+ ```python
454
+ class ReportTemplate(ApiModel):
455
+ id: Optional[int] # Unique template identifier (API key: "Id")
456
+ title: Optional[str] # Template name
457
+ service: Optional[str] # Service name (e.g., "Webex Calling")
458
+ max_days: Optional[int] # Maximum date range allowed
459
+ start_date: Optional[date] # Earliest available data date
460
+ end_date: Optional[date] # Latest available data date
461
+ identifier: Optional[str] # Template reference key
462
+ validations: Optional[list[ValidationRules]] # Required fields for report creation
463
+ ```
464
+
465
+ ### Standard Webex Calling Report Templates
466
+
467
+ | Template Name | Description | Key Metrics |
468
+ |---------------|-------------|-------------|
469
+ | **Detailed Call History** | Comprehensive call log with timestamps, duration, status, parties | All CDR fields (see Section 1) |
470
+ | **Calling Media Quality** | Per-call-leg quality measurements | Latency, jitter, packet loss |
471
+ | **Calling Engagement** | Usage and adoption tracking | Call volume, usage patterns, adoption rates over time |
472
+ | **Calling Quality** | Client-side quality from Webex Calling app | Jitter, latency, packet loss (client perspective) |
473
+ | **Call Queue Stats** | Queue-level performance metrics | Incoming calls, wait times, abandonment rates, handling efficiency |
474
+ | **Call Queue Agent Stats** | Per-agent queue performance | Calls handled, average handle time, service level achievements |
475
+ | **Auto-attendant Stats Summary** | AA call volume and menu usage | Call volume, handling metrics, caller menu selections |
476
+ | **Auto-attendant Business & After-Hours Key Details** | AA interaction patterns by time period | Business hours vs. after-hours key press patterns |
477
+
478
+ **Note:** Template IDs are dynamic and may vary by organization. Always use `list_templates()` (or the `GET /report/templates` endpoint) to discover IDs at runtime. The OpenAPI spec example shows ID 130, but actual IDs should not be hardcoded.
479
+
480
+ ### CLI Examples
481
+
482
+ ```bash
483
+ # List all available report templates (JSON output)
484
+ wxcli report-templates show
485
+
486
+ # List report templates in table format
487
+ wxcli report-templates show -o table
488
+ ```
489
+
490
+ ### Usage Example
491
+
492
+ ```python
493
+ templates = api.reports.list_templates()
494
+ calling_templates = [t for t in templates if t.service and 'Calling' in t.service]
495
+ for t in calling_templates:
496
+ print(f"ID: {t.id} | {t.title} | max {t.max_days} days")
497
+ ```
498
+
499
+ ---
500
+
501
+ ## 3. Reports API
502
+
503
+ Create, list, poll, download, and delete generated reports. Reports are CSV files delivered as ZIP archives.
504
+
505
+ ### Constraints
506
+
507
+ - **Maximum 50 reports** can exist at any time. Delete old reports to free quota.
508
+ - Each report can be downloaded up to 30 times.
509
+ - CSV reports for Webex services are only supported for **North American** organizations. Other regions return blank CSVs for Webex-service reports.
510
+ - Reports are delivered in **ZIP format** (Content-Type: `application/zip` or `application/octet-stream`).
511
+
512
+ ### CLI Examples
513
+
514
+ ```bash
515
+ # Create a report from a template (e.g., template ID 130 = Call Queue Stats)
516
+ wxcli reports create --template-id 130 \
517
+ --start-date "2026-03-01" --end-date "2026-03-15"
518
+
519
+ # Create a Detailed Call History report
520
+ wxcli reports create --template-id 25 \
521
+ --start-date "2026-03-01" --end-date "2026-03-07"
522
+
523
+ # Create a report using full JSON body
524
+ wxcli reports create --json-body '{"templateId": 130, "startDate": "2026-03-01", "endDate": "2026-03-15"}'
525
+ ```
526
+
527
+ **Note:** The CLI `reports` group has `list`, `create`, `show` (poll status/get download URL), and `delete`. To download report CSVs after getting the `downloadURL` from `wxcli reports show`, use `curl` or the SDK `ReportsApi.download()` method.
528
+
529
+ ### Create a Report
530
+
531
+ ```python
532
+ ReportsApi.create(
533
+ template_id: int, # From list_templates()
534
+ start_date: date = None, # Data start date (YYYY-MM-DD)
535
+ end_date: date = None, # Data end date (YYYY-MM-DD)
536
+ site_list: str = None # Required for site-based templates (Webex Meetings)
537
+ ) -> str # Returns report ID
538
+ ```
539
+
540
+ **REST equivalent:** `POST https://webexapis.com/v1/reports`
541
+
542
+ **Request body:**
543
+ ```json
544
+ {
545
+ "templateId": 130,
546
+ "startDate": "2024-02-01",
547
+ "endDate": "2024-02-05"
548
+ }
549
+ ```
550
+
551
+ **Response:**
552
+ ```json
553
+ {
554
+ "items": {
555
+ "Id": "Y2lz...ZA"
556
+ }
557
+ }
558
+ ```
559
+
560
+ ### List Reports
561
+
562
+ ```python
563
+ ReportsApi.list(
564
+ report_id: str = None, # Filter by report ID
565
+ service: str = None, # Filter by service name
566
+ template_id: str = None, # Filter by template ID
567
+ from_date: date = None, # Reports created on or after this date
568
+ to_date: date = None # Reports created before this date
569
+ ) -> Generator[Report, None, None]
570
+ ```
571
+
572
+ **REST equivalent:** `GET https://webexapis.com/v1/reports`
573
+
574
+ Note: `from_date` and `to_date` must be provided together.
575
+
576
+ ### Get Report Details (Poll Status)
577
+
578
+ ```python
579
+ ReportsApi.details(
580
+ report_id: str # Report ID from create()
581
+ ) -> Report
582
+ ```
583
+
584
+ **REST equivalent:** `GET https://webexapis.com/v1/reports/{reportId}`
585
+
586
+ ### Report Model
587
+
588
+ ```python
589
+ class Report(ApiModel):
590
+ id: Optional[str] # Report identifier (API key: "Id")
591
+ title: Optional[str] # Report template name
592
+ service: Optional[str] # Service name
593
+ start_date: Optional[date] # Data range start
594
+ end_date: Optional[date] # Data range end
595
+ site_list: Optional[str] # Site (Webex Meetings only)
596
+ created: Optional[datetime] # Creation timestamp
597
+ created_by: Optional[str] # Creator's user ID
598
+ schedule_from: Optional[str] # "api" or "controlHub"
599
+ status: Optional[str] # "done" or "In progress"
600
+ download_domain: Optional[str] # Download host
601
+ download_url: Optional[str] # Full download URL (API key: "downloadURL")
602
+ ```
603
+
604
+ **Status values:**
605
+ - `"done"` — Report is ready for download. `download_url` is populated.
606
+ - `"In progress"` — Report is still generating. Poll again.
607
+
608
+ ### Download a Report
609
+
610
+ ```python
611
+ ReportsApi.download(
612
+ url: str # The download_url from Report details
613
+ ) -> Generator[dict, None, None]
614
+ ```
615
+
616
+ The SDK handles:
617
+ 1. Authenticated GET request to the download URL
618
+ 2. Reading the ZIP archive from the response
619
+ 3. Extracting the first CSV file
620
+ 4. Skipping the UTF-8 BOM (3 bytes)
621
+ 5. Parsing CSV rows into dicts via `csv.DictReader`
622
+ 6. Yielding each row as a `dict`
623
+
624
+ ### Delete a Report
625
+
626
+ ```python
627
+ ReportsApi.delete(
628
+ report_id: str # Report ID to remove
629
+ )
630
+ ```
631
+
632
+ **REST equivalent:** `DELETE https://webexapis.com/v1/reports/{reportId}`
633
+
634
+ ### CallingCDR — Typed Report Download
635
+
636
+ For Detailed Call History reports specifically, use `CallingCDR.from_dicts()` to get typed CDR objects instead of raw dicts:
637
+
638
+ ```python
639
+ from wxc_sdk.reports import CallingCDR
640
+
641
+ # Get the download URL from a completed report
642
+ report = api.reports.details(report_id='<id>')
643
+ assert report.status == 'done'
644
+
645
+ # Download and parse into typed CDR objects
646
+ cdrs = list(CallingCDR.from_dicts(api.reports.download(url=report.download_url)))
647
+ for cdr in cdrs:
648
+ print(f"{cdr.start_time} | {cdr.user} | {cdr.duration}s | {cdr.call_outcome}")
649
+ ```
650
+
651
+ `CallingCDR` extends `CDR` — all 55+ fields from Section 1 are available.
652
+
653
+ ---
654
+
655
+ ## 4. Complete Workflow: Generate and Download a Report
656
+
657
+ ### CLI Workflow
658
+
659
+ ```bash
660
+ # Step 1: Discover available templates
661
+ wxcli report-templates show -o json
662
+
663
+ # Step 2: Create the report (use the template ID from step 1)
664
+ wxcli reports create --template-id 130 \
665
+ --start-date "2026-03-01" --end-date "2026-03-15"
666
+ # Note the report ID from the response
667
+
668
+ # Step 3: Poll until status is "done"
669
+ wxcli reports show REPORT_ID -o json
670
+ # Look for "status": "done" and "downloadURL" in the response
671
+
672
+ # Step 4: Download the CSV ZIP from the downloadURL
673
+ # Use curl or the SDK ReportsApi.download() method
674
+
675
+ # Step 5: Delete the report to free quota (max 50 reports)
676
+ wxcli reports delete REPORT_ID --force
677
+ ```
678
+
679
+ ### SDK Workflow
680
+
681
+ ```python
682
+ from wxc_sdk import WebexSimpleApi
683
+ from datetime import date
684
+ import time
685
+
686
+ api = WebexSimpleApi(tokens='<access_token>')
687
+
688
+ # Step 1: Find the template
689
+ templates = api.reports.list_templates()
690
+ cq_template = next(t for t in templates if t.title and 'Call Queue Stats' in t.title)
691
+ print(f"Using template: {cq_template.id} — {cq_template.title} (max {cq_template.max_days} days)")
692
+
693
+ # Step 2: Create the report
694
+ report_id = api.reports.create(
695
+ template_id=cq_template.id,
696
+ start_date=date(2024, 2, 1),
697
+ end_date=date(2024, 2, 28)
698
+ )
699
+ print(f"Report created: {report_id}")
700
+
701
+ # Step 3: Poll until done
702
+ while True:
703
+ report = api.reports.details(report_id=report_id)
704
+ print(f"Status: {report.status}")
705
+ if report.status == 'done':
706
+ break
707
+ time.sleep(30)
708
+
709
+ # Step 4: Download
710
+ rows = list(api.reports.download(url=report.download_url))
711
+ print(f"Downloaded {len(rows)} rows")
712
+
713
+ # Step 5: Clean up (free the 50-report quota)
714
+ api.reports.delete(report_id=report_id)
715
+ ```
716
+
717
+ ---
718
+
719
+ ## 5. CDR Feed vs. Reports API — When to Use Which
720
+
721
+ | Criteria | CDR Feed API | Reports API |
722
+ |----------|-------------|-------------|
723
+ | **Data freshness** | Near real-time (5-minute delay) | Batch (async generation) |
724
+ | **Time range** | Last 30 days, 12-hour window per request | Depends on template `max_days` |
725
+ | **Output format** | JSON (paginated) | CSV in ZIP archive |
726
+ | **Use case** | Live dashboards, recent call lookup, alerting | Historical analysis, scheduled reports, compliance |
727
+ | **Rate limit** | 1 req/min + 10 pagination/min | Standard Webex API limits |
728
+ | **Scope** | `spark-admin:calling_cdr_read` | `analytics:read_all` |
729
+ | **License** | Admin role required | Pro Pack required |
730
+ | **Data fields** | 55+ CDR fields (JSON) | Varies by template (CSV columns) |
731
+
732
+ ---
733
+
734
+ ## 6. Use Cases
735
+
736
+ ### Call Quality Monitoring
737
+
738
+ Use the **CDR Feed** for near-real-time monitoring, or the **Calling Media Quality** / **Calling Quality** report templates for historical analysis.
739
+
740
+ ```bash
741
+ # Pull recent CDR records to check for failures (adjust times to last hour)
742
+ wxcli cdr list \
743
+ --start-time "2026-03-17T15:00:00.000Z" \
744
+ --end-time "2026-03-17T16:00:00.000Z" -o json
745
+
746
+ # Create a Calling Media Quality report for historical analysis
747
+ wxcli reports create --template-id 26 \
748
+ --start-date "2026-03-01" --end-date "2026-03-15"
749
+ ```
750
+
751
+ ```python
752
+ # Find calls with poor outcomes in the last hour
753
+ from datetime import datetime, timedelta, timezone
754
+
755
+ end = datetime.now(timezone.utc) - timedelta(minutes=5)
756
+ start = end - timedelta(hours=1)
757
+
758
+ failed_calls = [
759
+ cdr for cdr in api.cdr.get_cdr_history(start_time=start, end_time=end)
760
+ if cdr.call_outcome in ('Failure', 'Refusal')
761
+ ]
762
+ for c in failed_calls:
763
+ print(f"{c.start_time} | {c.user} | {c.call_outcome}: {c.call_outcome_reason}")
764
+ ```
765
+
766
+ ### Agent Performance (Call Queue Agent Stats)
767
+
768
+ Generate a **Call Queue Agent Stats** report:
769
+
770
+ ```bash
771
+ # Create a Call Queue Agent Stats report
772
+ wxcli reports create --template-id 131 \
773
+ --start-date "2026-02-01" --end-date "2026-02-28"
774
+ ```
775
+
776
+ ```python
777
+ agent_template = next(t for t in templates if t.title and 'Agent Stats' in t.title)
778
+ report_id = api.reports.create(
779
+ template_id=agent_template.id,
780
+ start_date=date(2024, 2, 1),
781
+ end_date=date(2024, 2, 28)
782
+ )
783
+ # Poll, download, analyze per-agent metrics
784
+ ```
785
+
786
+ ### Queue Analytics (Call Queue Stats)
787
+
788
+ Generate a **Call Queue Stats** report for queue-level KPIs: incoming call volume, wait times, abandonment rates, handling efficiency.
789
+
790
+ ```bash
791
+ # Create a Call Queue Stats report
792
+ wxcli reports create --template-id 130 \
793
+ --start-date "2026-02-01" --end-date "2026-02-28"
794
+ ```
795
+
796
+ ### Auto-Attendant Analytics
797
+
798
+ Use **Auto-attendant Stats Summary** for overall AA performance, or **Auto-attendant Business & After-Hours Key Details** to analyze DTMF key press patterns across business hours vs. after-hours.
799
+
800
+ The CDR field `auto_attendant_key_pressed` is also available in real-time CDR records.
801
+
802
+ ```bash
803
+ # Create an Auto-Attendant Stats Summary report
804
+ wxcli reports create --template-id 132 \
805
+ --start-date "2026-02-01" --end-date "2026-02-28"
806
+ ```
807
+
808
+ ### Billing / Cost Analysis
809
+
810
+ Filter CDR records by `call_type` to separate international, toll-free, premium, and national calls:
811
+
812
+ ```python
813
+ international = [
814
+ cdr for cdr in api.cdr.get_cdr_history(start_time=start, end_time=end)
815
+ if cdr.call_type and cdr.call_type.value == 'SIP_INTERNATIONAL'
816
+ ]
817
+ ```
818
+
819
+ PSTN vendor fields (`pstn_vendor_name`, `pstn_legal_entity`, `pstn_provider_id`) provide carrier-level detail for cost attribution.
820
+
821
+ ```bash
822
+ # Pull CDR records and filter by international calls using JSON output + jq
823
+ wxcli cdr list \
824
+ --start-time "2026-03-17T14:00:00.000Z" \
825
+ --end-time "2026-03-17T16:00:00.000Z" -o json
826
+ ```
827
+
828
+ ### Call Recording Audit
829
+
830
+ Filter by recording fields to audit recording compliance:
831
+
832
+ ```bash
833
+ # Pull CDR records to audit recording outcomes
834
+ wxcli cdr list \
835
+ --start-time "2026-03-17T14:00:00.000Z" \
836
+ --end-time "2026-03-17T16:00:00.000Z" -o json
837
+
838
+ # List calling recordings to check status
839
+ wxcli recordings list --service-type calling --status available
840
+
841
+ # Get recording audit summaries for compliance review
842
+ wxcli recording-report list \
843
+ --from "2026-03-01T00:00:00Z" --to "2026-03-17T00:00:00Z"
844
+ ```
845
+
846
+ ```python
847
+ failed_recordings = [
848
+ cdr for cdr in api.cdr.get_cdr_history(start_time=start, end_time=end)
849
+ if cdr.call_recording_result == 'failed'
850
+ ]
851
+ ```
852
+
853
+ ---
854
+
855
+ ## 7. Known API Documentation Bugs (from SDK source)
856
+
857
+ The SDK source code flags several discrepancies between Webex API documentation and actual behavior:
858
+
859
+ 1. **Report Templates response:** Documentation says `"Template Attributes"` but actual key is `"items"`.
860
+ 2. **Report Templates `id` field:** Documentation says `"id"` but actual key is `"Id"`.
861
+ 3. **Report Templates missing fields:** `startDate` and `endDate` are not documented but are returned.
862
+ 4. **Report Templates validations:** Documentation nests as `"validations"/"validations"` but actual structure is flat `"validations"`.
863
+ 5. **List Reports response:** Documentation says `"Report Attributes"` but actual key is `"items"`.
864
+ 6. **List Reports `scheduledFrom`:** Documentation says `"scheduledFrom"` but actual key is `"scheduleFrom"`.
865
+ 7. **List Reports missing field:** `downloadDomain` is returned but not documented.
866
+ 8. **Create Report response:** Actual response is `{"items": {"Id": "..."}}`, not documented structure.
867
+
868
+ ---
869
+
870
+ ## 8. Gotchas
871
+
872
+ - **Regional routing:** If the CDR endpoint returns HTTP 451, parse the response body for the correct regional endpoint URL and retry.
873
+ - **12-hour window limit:** CDR Feed requests cannot span more than 12 hours. For longer ranges, issue multiple sequential requests.
874
+ - **Empty/NA normalization:** The SDK converts empty strings and `"NA"` values to `None` automatically.
875
+ - **Field name aliasing:** Several CDR fields use non-standard aliases in the API (e.g., `"Route list calls overage"`, `"Original Called Party UUID"`, `"Queue Type"`). The SDK handles these via Pydantic `Field(alias=...)`.
876
+ - **Timezone:** All CDR timestamps are in UTC. The `site_timezone` field provides the offset in minutes if you need to convert to the user's local time.
877
+ - **Report quota:** The 50-report limit is hard. Always delete reports after downloading to avoid hitting the cap.
878
+ - **Pro Pack requirement:** The Reports API (templates, create, list, download, delete) requires the Pro Pack license. The CDR Feed API does not require Pro Pack but does require the admin role to be explicitly enabled.
879
+ - **Async download not implemented:** The SDK's async variant of `ReportsApi.download()` raises `NotImplementedError`. Use the sync API for report downloads.
880
+ - **`transfer_related_call_id` contains a `local_call_id`, NOT a Correlation ID.** To link two Correlation IDs across a transfer: find CDRs where `CDR1.transfer_related_call_id = CDR2.local_call_id` — their Correlation IDs are linked. Do not compare `transfer_related_call_id` directly to `correlation_id`.
881
+ - **HG CDRs have both ORIGINATING and TERMINATING records.** The HuntGroup entity appears in CDRs as TERMINATING (receiving the inbound call) and ORIGINATING (dialing each agent). `Answered=true` on a HuntGroup CDR means the HG processed the call, not that a human answered. Do not count HG-typed CDRs when tallying human-answered calls.
882
+ - **A single call can have multiple Correlation IDs.** Attended transfers, on-prem deflections that return to cloud, and some advanced forwarding scenarios create new Correlation IDs. Use `transfer_related_call_id`/`local_call_id` linkage to reconstruct the full call path.
883
+ - **`related_call_id` is for deflection chaining, not just service activation.** Each HG ring attempt's ORIGINATING CDR has `related_call_id` = the HG TERMINATING CDR's `local_call_id`. This is how you link delivery attempts to the HG that distributed them. `related_call_id` is ALSO used when a new separate call is spawned by service activation (recording, exec-assistant).
884
+ - **On-prem correlation uses Session IDs.** When a call crosses the LGW boundary, the cloud CDR's `local_session_id`/`remote_session_id` matches the UCM CDR's Origination/Destination SIP Session ID. This is the only way to correlate cloud and on-prem (UCM) records for the same call — the Correlation ID does not appear in UCM CDRs.
885
+
886
+ ---
887
+
888
+ ## 9. Raw HTTP Endpoints
889
+ <!-- Updated by playbook session 2026-03-18 -->
890
+
891
+ All endpoints below use the `api.session.rest_*` methods from `wxc_sdk`. URLs confirmed from working CLI implementations.
892
+
893
+ ```python
894
+ from wxc_sdk import WebexSimpleApi
895
+ api = WebexSimpleApi(tokens='<token>')
896
+ BASE = "https://webexapis.com/v1"
897
+ ```
898
+
899
+ ### Detailed Call History (CDR)
900
+
901
+ **Important:** The CDR endpoints use a different base URL than the standard Webex API. `startTime` and `endTime` are required and must be in ISO 8601 format (e.g., `2026-03-17T14:00:00.000Z`). The time window cannot exceed 12 hours.
902
+
903
+ #### CDR Feed (Batch/Historical)
904
+
905
+ ```
906
+ GET https://webexapis.com/v1/cdr_feed
907
+ ```
908
+
909
+ ```python
910
+ params = {
911
+ "startTime": "2026-03-17T14:00:00.000Z", # ISO 8601 — required
912
+ "endTime": "2026-03-17T16:00:00.000Z", # ISO 8601 — required
913
+ # "locations": "San Jose,Austin", # optional, up to 10 comma-separated
914
+ "max": 1000 # no auto-pagination
915
+ }
916
+ result = api.session.rest_get(f"{BASE}/cdr_feed", params=params)
917
+ # Returns: {"items": [{"Start time": "...", "Answer time": "...", ...}, ...]}
918
+ ```
919
+
920
+ #### CDR Stream (Near-Real-Time)
921
+
922
+ ```
923
+ GET https://webexapis.com/v1/cdr_stream
924
+ ```
925
+
926
+ ```python
927
+ params = {
928
+ "startTime": "2026-03-17T14:00:00.000Z",
929
+ "endTime": "2026-03-17T16:00:00.000Z",
930
+ "max": 1000
931
+ }
932
+ result = api.session.rest_get(f"{BASE}/cdr_stream", params=params)
933
+ # Same response format as cdr_feed
934
+ ```
935
+
936
+ ### Report Templates
937
+
938
+ ```
939
+ GET https://webexapis.com/v1/report/templates
940
+ ```
941
+
942
+ ```python
943
+ result = api.session.rest_get(f"{BASE}/report/templates")
944
+ # Returns: {"items": [{"Id": 130, "title": "Call Queue Stats", "service": "Webex Calling", ...}, ...]}
945
+ # Note: API returns "Id" (capital I), not "id"
946
+ ```
947
+
948
+ ### Reports
949
+
950
+ #### List Reports
951
+
952
+ ```
953
+ GET https://webexapis.com/v1/reports
954
+ ```
955
+
956
+ ```python
957
+ params = {
958
+ # "reportId": "report_id",
959
+ # "service": "Webex Calling",
960
+ # "templateId": "130",
961
+ # "from": "2026-03-01", # from and to must be provided together
962
+ # "to": "2026-03-17",
963
+ "max": 1000
964
+ }
965
+ result = api.session.rest_get(f"{BASE}/reports", params=params)
966
+ ```
967
+
968
+ #### Get Report Details
969
+
970
+ ```
971
+ GET https://webexapis.com/v1/reports/{reportId}
972
+ ```
973
+
974
+ ```python
975
+ result = api.session.rest_get(f"{BASE}/reports/{report_id}")
976
+ # Returns: {"Id": "...", "title": "...", "status": "done", "downloadURL": "https://...", ...}
977
+ # Poll until status == "done", then use downloadURL to fetch the CSV ZIP
978
+ ```
979
+
980
+ #### Create a Report
981
+
982
+ ```
983
+ POST https://webexapis.com/v1/reports
984
+ ```
985
+
986
+ ```python
987
+ body = {
988
+ "templateId": 130, # from report/templates list
989
+ "startDate": "2026-03-01", # YYYY-MM-DD
990
+ "endDate": "2026-03-15" # YYYY-MM-DD
991
+ # "siteList": "site_url" # required only for Webex Meetings templates
992
+ }
993
+ result = api.session.rest_post(f"{BASE}/reports", json=body)
994
+ # Returns: {"items": {"Id": "report_id_here"}}
995
+ ```
996
+
997
+ #### Delete a Report
998
+
999
+ ```
1000
+ DELETE https://webexapis.com/v1/reports/{reportId}
1001
+ ```
1002
+
1003
+ ```python
1004
+ api.session.rest_delete(f"{BASE}/reports/{report_id}")
1005
+ ```
1006
+
1007
+ ### Converged Recordings
1008
+
1009
+ #### CLI Examples
1010
+
1011
+ ```bash
1012
+ # List all available recordings
1013
+ wxcli recordings list
1014
+
1015
+ # List calling recordings in a date range
1016
+ wxcli recordings list \
1017
+ --from "2026-03-01T00:00:00Z" --to "2026-03-17T00:00:00Z" \
1018
+ --service-type calling
1019
+
1020
+ # List recordings filtered by status and format
1021
+ wxcli recordings list --status available --format MP3
1022
+
1023
+ # List recordings by location and owner type
1024
+ wxcli recordings list --location-id "Y2lz...bG9j" --owner-type user
1025
+
1026
+ # List recordings in JSON format with pagination
1027
+ wxcli recordings list --service-type calling -o json --limit 50
1028
+
1029
+ # List recordings for admin/compliance (all users in org)
1030
+ wxcli recordings list-converged-recordings \
1031
+ --from "2026-03-01T00:00:00Z" --to "2026-03-17T00:00:00Z"
1032
+
1033
+ # Get details for a specific recording
1034
+ wxcli recordings show "Y2lz...cmVj"
1035
+
1036
+ # Get recording metadata
1037
+ wxcli recordings show-metadata "Y2lz...cmVj"
1038
+
1039
+ # Delete a recording
1040
+ wxcli recordings delete "Y2lz...cmVj"
1041
+
1042
+ # Reassign recordings from one owner to another
1043
+ wxcli recordings create \
1044
+ --owner-email "oldowner@company.com" \
1045
+ --reassign-owner-email "newowner@company.com"
1046
+
1047
+ # Move recordings to recycle bin
1048
+ wxcli recordings create-soft-delete \
1049
+ --json-body '{"trashAll": true, "ownerEmail": "user@company.com"}'
1050
+
1051
+ # Restore recordings from recycle bin
1052
+ wxcli recordings create-restore \
1053
+ --json-body '{"restoreAll": true, "ownerEmail": "user@company.com"}'
1054
+
1055
+ # Purge recordings permanently from recycle bin
1056
+ wxcli recordings create-purge \
1057
+ --json-body '{"purgeAll": true, "ownerEmail": "user@company.com"}'
1058
+
1059
+ # Download a single recording's artifacts (transcript, AI notes, audio)
1060
+ wxcli converged-recordings download "Y2lz...cmVj"
1061
+ wxcli converged-recordings download "Y2lz...cmVj" --include-audio
1062
+ wxcli converged-recordings download "Y2lz...cmVj" -d /tmp/my-recordings
1063
+
1064
+ # Bulk export recordings to JSONL (BI-ready)
1065
+ wxcli converged-recordings export \
1066
+ --from "2026-03-01T00:00:00Z" --to "2026-03-28T00:00:00Z"
1067
+
1068
+ # Export with filters
1069
+ wxcli converged-recordings export \
1070
+ --from "2026-03-01T00:00:00Z" --to "2026-03-28T00:00:00Z" \
1071
+ --owner-email user@company.com --service-type calling
1072
+
1073
+ # Export as individual files per recording
1074
+ wxcli converged-recordings export \
1075
+ --from "2026-03-01T00:00:00Z" --to "2026-03-28T00:00:00Z" \
1076
+ --format json-per-file
1077
+
1078
+ # Export with audio files
1079
+ wxcli converged-recordings export \
1080
+ --from "2026-03-01T00:00:00Z" --to "2026-03-28T00:00:00Z" \
1081
+ --include-audio -d /data/recording-export
1082
+ ```
1083
+
1084
+ #### List Recordings (User)
1085
+
1086
+ ```
1087
+ GET https://webexapis.com/v1/convergedRecordings
1088
+ ```
1089
+
1090
+ ```python
1091
+ params = {
1092
+ # "from": "2026-03-01T00:00:00Z",
1093
+ # "to": "2026-03-17T00:00:00Z",
1094
+ # "status": "available",
1095
+ # "serviceType": "calling",
1096
+ # "format": "MP3",
1097
+ # "ownerType": "user",
1098
+ # "storageRegion": "US",
1099
+ # "locationId": "location_id",
1100
+ # "topic": "search_term",
1101
+ "max": 1000
1102
+ }
1103
+ result = api.session.rest_get(f"{BASE}/convergedRecordings", params=params)
1104
+ ```
1105
+
1106
+ #### List Recordings (Admin/Compliance)
1107
+
1108
+ ```
1109
+ GET https://webexapis.com/v1/admin/convergedRecordings
1110
+ ```
1111
+
1112
+ ```python
1113
+ params = {
1114
+ # "ownerId": "user_id",
1115
+ # "ownerEmail": "user@company.com",
1116
+ # All same params as user endpoint above
1117
+ "max": 1000
1118
+ }
1119
+ result = api.session.rest_get(f"{BASE}/admin/convergedRecordings", params=params)
1120
+ ```
1121
+
1122
+ #### Get Recording Details
1123
+
1124
+ ```
1125
+ GET https://webexapis.com/v1/convergedRecordings/{recordingId}
1126
+ ```
1127
+
1128
+ ```python
1129
+ result = api.session.rest_get(f"{BASE}/convergedRecordings/{recording_id}")
1130
+ ```
1131
+
1132
+ #### Delete a Recording
1133
+
1134
+ ```
1135
+ DELETE https://webexapis.com/v1/convergedRecordings/{recordingId}
1136
+ ```
1137
+
1138
+ ```python
1139
+ api.session.rest_delete(f"{BASE}/convergedRecordings/{recording_id}")
1140
+ ```
1141
+
1142
+ #### Get Recording Metadata
1143
+
1144
+ ```
1145
+ GET https://webexapis.com/v1/convergedRecordings/{recordingId}/metadata
1146
+ ```
1147
+
1148
+ ```python
1149
+ params = {"showAllTypes": "true"} # optional — show all attribute types
1150
+ result = api.session.rest_get(f"{BASE}/convergedRecordings/{recording_id}/metadata", params=params)
1151
+ ```
1152
+
1153
+ #### Reassign Recordings
1154
+
1155
+ ```
1156
+ POST https://webexapis.com/v1/convergedRecordings/reassign
1157
+ ```
1158
+
1159
+ ```python
1160
+ body = {
1161
+ "reassignOwnerEmail": "newowner@company.com",
1162
+ "ownerEmail": "oldowner@company.com"
1163
+ # or "ownerID": "user_id"
1164
+ }
1165
+ result = api.session.rest_post(f"{BASE}/convergedRecordings/reassign", json=body)
1166
+ ```
1167
+
1168
+ #### Move Recordings to Recycle Bin
1169
+
1170
+ ```
1171
+ POST https://webexapis.com/v1/convergedRecordings/softDelete
1172
+ ```
1173
+
1174
+ ```python
1175
+ body = {
1176
+ "trashAll": True,
1177
+ "ownerEmail": "user@company.com"
1178
+ }
1179
+ result = api.session.rest_post(f"{BASE}/convergedRecordings/softDelete", json=body)
1180
+ ```
1181
+
1182
+ #### Restore Recordings from Recycle Bin
1183
+
1184
+ ```
1185
+ POST https://webexapis.com/v1/convergedRecordings/restore
1186
+ ```
1187
+
1188
+ ```python
1189
+ body = {
1190
+ "restoreAll": True,
1191
+ "ownerEmail": "user@company.com"
1192
+ }
1193
+ result = api.session.rest_post(f"{BASE}/convergedRecordings/restore", json=body)
1194
+ ```
1195
+
1196
+ #### Download Recording Artifacts
1197
+
1198
+ Downloads a single recording's transcript, AI-generated notes, and optionally the MP3 audio file to a local directory. Uses the `temporaryDirectDownloadLinks` from the recording detail response. The download URLs are pre-signed — they are fetched directly with HTTP GET, no Bearer token needed.
1199
+
1200
+ ```bash
1201
+ wxcli converged-recordings download RECORDING_ID [--include-audio] [-d OUTPUT_DIR]
1202
+ ```
1203
+
1204
+ Output structure:
1205
+ ```
1206
+ {output_dir}/{recording_id}/
1207
+ metadata.json # full recording detail response
1208
+ transcript.txt # if available
1209
+ suggested_notes.html # if available
1210
+ short_notes.html # if available
1211
+ action_items.html # if available
1212
+ audio.mp3 # if --include-audio and available
1213
+ ```
1214
+
1215
+ #### Bulk Export Recordings
1216
+
1217
+ Paginates the admin listing endpoint, fetches detail for each recording, and downloads all text/AI artifacts. Produces either a single JSONL file (BI-ready) or one directory per recording.
1218
+
1219
+ ```bash
1220
+ wxcli converged-recordings export --from START --to END [filters...] [--format jsonl|json-per-file]
1221
+ ```
1222
+
1223
+ JSONL mode (default): writes `recordings.jsonl` with one JSON object per line containing all metadata and inline text content. Audio files (if `--include-audio`) go to `{output_dir}/audio/{recording_id}.mp3`.
1224
+
1225
+ JSON-per-file mode: same directory structure as the `download` command, one directory per recording.
1226
+
1227
+ Processing is sequential — each recording is fully fetched and downloaded before moving to the next, since download links expire in 3 hours.
1228
+
1229
+ #### Purge Recordings from Recycle Bin
1230
+
1231
+ ```
1232
+ POST https://webexapis.com/v1/convergedRecordings/purge
1233
+ ```
1234
+
1235
+ ```python
1236
+ body = {
1237
+ "purgeAll": True,
1238
+ "ownerEmail": "user@company.com"
1239
+ }
1240
+ result = api.session.rest_post(f"{BASE}/convergedRecordings/purge", json=body)
1241
+ ```
1242
+
1243
+ ### Recording Reports
1244
+
1245
+ #### CLI Examples
1246
+
1247
+ ```bash
1248
+ # List recording audit report summaries
1249
+ wxcli recording-report list \
1250
+ --from "2026-03-01T00:00:00Z" --to "2026-03-17T00:00:00Z"
1251
+
1252
+ # List recording audit summaries for a specific host
1253
+ wxcli recording-report list --host-email "host@company.com"
1254
+
1255
+ # Get recording audit report access details
1256
+ wxcli recording-report list-access-detail --recording-id "Y2lz...cmVj"
1257
+
1258
+ # List meeting archive summaries
1259
+ wxcli recording-report list-meeting-archive-summaries
1260
+
1261
+ # Get meeting archive details
1262
+ wxcli recording-report show "Y2lz...YXJj"
1263
+ ```
1264
+
1265
+ #### Access Summary
1266
+
1267
+ ```
1268
+ GET https://webexapis.com/v1/recordingReport/accessSummary
1269
+ ```
1270
+
1271
+ ```python
1272
+ params = {
1273
+ # "recordingId": "recording_id",
1274
+ "max": 1000
1275
+ }
1276
+ result = api.session.rest_get(f"{BASE}/recordingReport/accessSummary", params=params)
1277
+ ```
1278
+
1279
+ #### Access Detail
1280
+
1281
+ ```
1282
+ GET https://webexapis.com/v1/recordingReport/accessDetail
1283
+ ```
1284
+
1285
+ ```python
1286
+ params = {
1287
+ # "recordingId": "recording_id",
1288
+ "max": 1000
1289
+ }
1290
+ result = api.session.rest_get(f"{BASE}/recordingReport/accessDetail", params=params)
1291
+ ```
1292
+
1293
+ #### Meeting Archive Summaries
1294
+
1295
+ ```
1296
+ GET https://webexapis.com/v1/recordingReport/meetingArchiveSummaries
1297
+ ```
1298
+
1299
+ ```python
1300
+ result = api.session.rest_get(f"{BASE}/recordingReport/meetingArchiveSummaries", params={"max": 1000})
1301
+ ```
1302
+
1303
+ #### Meeting Archive Detail
1304
+
1305
+ ```
1306
+ GET https://webexapis.com/v1/recordingReport/meetingArchives/{archiveId}
1307
+ ```
1308
+
1309
+ ```python
1310
+ result = api.session.rest_get(f"{BASE}/recordingReport/meetingArchives/{archive_id}")
1311
+ ```
1312
+
1313
+ ---
1314
+
1315
+ ## See Also
1316
+
1317
+ - **[authentication.md](authentication.md)** — OAuth scopes and token management. CDR access requires `spark-admin:calling_cdr_read` with an explicit admin role; Reports API requires `analytics:read_all` with Pro Pack.
1318
+ - **[call-features-major.md](call-features-major.md)** — Call Queue and Auto Attendant configuration. CDR fields like `queue_type`, `auto_attendant_key_pressed`, and queue-related report templates correspond to features configured there.