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,1898 @@
1
+ <!-- Updated by playbook session 2026-03-18 -->
2
+ # Person Call Settings -- Voicemail, Caller ID, Privacy & Recording
3
+
4
+ Reference for per-person (also virtual line and workspace) call settings covering voicemail, caller ID, anonymous call rejection, privacy, barge-in, call recording, call intercept, monitoring, push-to-talk, and music on hold.
5
+
6
+ All APIs in this group follow the same structural pattern: they extend `PersonSettingsApiChild`, which builds REST endpoints using the formula `people/{person_id}/features/{feature}` for persons (with alternate URL patterns for workspaces, virtual lines, and locations). Each sub-API exposes a `read()` and `configure()` method pair, sometimes with additional action methods.
7
+
8
+ ## Sources
9
+
10
+ - wxc_sdk v1.30.0 (PersonSettingsApi)
11
+ - OpenAPI spec: specs/webex-cloud-calling.json
12
+ - developer.webex.com Person Call Settings APIs
13
+
14
+ ## Required Scopes
15
+
16
+ | Feature | Read Scope | Write Scope | Notes |
17
+ |---------|-----------|------------|-------|
18
+ | Voicemail | `spark-admin:people_read` | `spark-admin:people_write` | Self: `spark:people_read` / `spark:people_write` |
19
+ | Voicemail Passcode | `spark-admin:telephony_config_write` | `spark-admin:telephony_config_write` | Write-only; uses `telephony/config/people` path |
20
+ | Voicemail PIN Reset | `spark-admin:people_write` | `spark-admin:people_write` | Write-only action |
21
+ | Caller ID | `spark-admin:people_read` | `spark-admin:people_write` | Self: `spark:people_read` / `spark:people_write` |
22
+ | Agent Caller ID | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` | Available caller IDs uses `spark-admin:people_read` |
23
+ | Anonymous Call Rejection | `spark-admin:workspaces_read` | `spark-admin:workspaces_write` | Workspace-only admin endpoint. No admin-level `/people/{id}` path exists; self-service via `/me` only. |
24
+ | Privacy | `spark-admin:people_read` | `spark-admin:people_write` | |
25
+ | Barge-In | `spark-admin:people_read` | `spark-admin:people_write` | Self: `spark:people_read` / `spark:people_write` |
26
+ | Call Recording | `spark-admin:people_read` | `spark-admin:people_write` | |
27
+ | Call Intercept | `spark-admin:people_read` | `spark-admin:people_write` | Greeting upload: self `spark:people_write` also works |
28
+ | Monitoring | `spark-admin:people_read` | `spark-admin:people_write` | |
29
+ | Push-to-Talk | `spark-admin:people_read` | `spark-admin:people_write` | |
30
+ | Music on Hold | `spark-admin:telephony_config_read` | `spark-admin:telephony_config_write` | Uses `telephony/config/people` path |
31
+
32
+ ---
33
+
34
+ ## Table of Contents
35
+
36
+ 1. [Voicemail](#1-voicemail)
37
+ 2. [Caller ID](#2-caller-id)
38
+ 3. [Agent Caller ID](#3-agent-caller-id)
39
+ 4. [Anonymous Call Rejection](#4-anonymous-call-rejection)
40
+ 5. [Privacy](#5-privacy)
41
+ 6. [Barge-In](#6-barge-in)
42
+ 7. [Call Recording](#7-call-recording)
43
+ 8. [Call Intercept](#8-call-intercept)
44
+ 9. [Monitoring](#9-monitoring)
45
+ 10. [Push-to-Talk](#10-push-to-talk)
46
+ 11. [Music on Hold](#11-music-on-hold)
47
+ 12. [Common Patterns](#12-common-patterns)
48
+
49
+ ---
50
+
51
+ ## 1. Voicemail
52
+
53
+ **API class:** `VoicemailApi`
54
+ **Feature key:** `voicemail`
55
+ **Source:** `wxc_sdk/person_settings/voicemail.py`
56
+
57
+ ### Data Models
58
+
59
+ #### `VoicemailSettings`
60
+
61
+ Top-level settings object returned by `read()` and passed to `configure()`.
62
+
63
+ | Field | Type | Description |
64
+ |-------|------|-------------|
65
+ | `enabled` | `bool` | Voicemail is enabled or disabled |
66
+ | `send_all_calls` | `VoicemailEnabled` | Settings for sending all calls to voicemail |
67
+ | `send_busy_calls` | `VoicemailEnabledWithGreeting` | Settings for sending calls to voicemail when line is busy |
68
+ | `send_unanswered_calls` | `UnansweredCalls` | Settings for calls sent to voicemail when unanswered |
69
+ | `notifications` | `VoicemailNotifications` | Settings for new voicemail notifications |
70
+ | `transfer_to_number` | `VoicemailTransferToNumber` | Transfer to a different number by pressing 0 |
71
+ | `email_copy_of_message` | `VoicemailCopyOfMessage` | Send copy of voicemail audio via email |
72
+ | `message_storage` | `VoicemailMessageStorage` | Message storage settings (internal vs. external) |
73
+ | `fax_message` | `VoicemailFax` | Fax message settings |
74
+ | `voice_message_forwarding_enabled` | `bool` | Read-only; excluded from updates |
75
+
76
+ #### `VoicemailEnabledWithGreeting` (extends `VoicemailEnabled`)
77
+
78
+ | Field | Type | Description |
79
+ |-------|------|-------------|
80
+ | `enabled` | `bool` | Whether this condition is active |
81
+ | `greeting` | `Greeting` | `DEFAULT` or `CUSTOM` (.wav file) |
82
+ | `greeting_uploaded` | `bool` | Read-only; indicates custom greeting exists |
83
+ | `audio_file` | `MediaFile` | Details of the custom audio file |
84
+
85
+ #### `UnansweredCalls` (extends `VoicemailEnabledWithGreeting`)
86
+
87
+ | Field | Type | Description |
88
+ |-------|------|-------------|
89
+ | `number_of_rings` | `int` | Rings before unanswered call goes to voicemail |
90
+ | `system_max_number_of_rings` | `int` | Read-only; system-wide max for number_of_rings |
91
+
92
+ #### Default Settings
93
+
94
+ `VoicemailSettings.default()` returns:
95
+
96
+ ```python
97
+ VoicemailSettings(
98
+ enabled=True,
99
+ send_all_calls=VoicemailEnabled(enabled=False),
100
+ send_busy_calls=VoicemailEnabledWithGreeting(enabled=False, greeting=Greeting.default),
101
+ send_unanswered_calls=UnansweredCalls(enabled=True, greeting=Greeting.default, number_of_rings=3),
102
+ notifications=VoicemailNotifications(enabled=False),
103
+ transfer_to_number=VoicemailTransferToNumber(enabled=False),
104
+ email_copy_of_message=VoicemailCopyOfMessage(enabled=False),
105
+ message_storage=VoicemailMessageStorage(mwi_enabled=True, storage_type=StorageType.internal),
106
+ fax_message=VoicemailFax(enabled=False),
107
+ voice_message_forwarding_enabled=False,
108
+ )
109
+ ```
110
+
111
+ ### Methods
112
+
113
+ #### `read`
114
+
115
+ ```python
116
+ VoicemailApi.read(entity_id: str, org_id: str = None) -> VoicemailSettings
117
+ ```
118
+
119
+ Retrieve voicemail settings for a person, virtual line, or workspace.
120
+
121
+ - **Scopes (read):** `spark-admin:people_read` (admin) or `spark:people_read` (self)
122
+
123
+ #### `configure`
124
+
125
+ ```python
126
+ VoicemailApi.configure(entity_id: str, settings: VoicemailSettings, org_id: str = None)
127
+ ```
128
+
129
+ Update voicemail settings. The `settings.update()` method automatically strips read-only fields (`greeting_uploaded`, `system_max_number_of_rings`, `voice_message_forwarding_enabled`) before sending.
130
+
131
+ - **Scopes (write):** `spark-admin:people_write` (admin) or `spark:people_write` (self)
132
+
133
+ #### `configure_busy_greeting`
134
+
135
+ ```python
136
+ VoicemailApi.configure_busy_greeting(
137
+ entity_id: str,
138
+ content: Union[BufferedReader, str],
139
+ upload_as: str = None,
140
+ org_id: str = None,
141
+ )
142
+ ```
143
+
144
+ Upload a custom `.wav` greeting for the busy condition. Uses multipart/form-data with `audio/wav` content type. Endpoint: `actions/uploadBusyGreeting/invoke`.
145
+
146
+ - `content` can be a file path (str) or an open binary reader.
147
+ - `upload_as` is required if `content` is a reader (must be a `.wav` filename).
148
+
149
+ #### `configure_no_answer_greeting`
150
+
151
+ ```python
152
+ VoicemailApi.configure_no_answer_greeting(
153
+ entity_id: str,
154
+ content: Union[BufferedReader, str],
155
+ upload_as: str = None,
156
+ org_id: str = None,
157
+ )
158
+ ```
159
+
160
+ Same as above but for the no-answer condition. Endpoint: `actions/uploadNoAnswerGreeting/invoke`.
161
+
162
+ #### `modify_passcode`
163
+
164
+ ```python
165
+ VoicemailApi.modify_passcode(entity_id: str, passcode: str, org_id: str = None)
166
+ ```
167
+
168
+ Set a new voicemail passcode. Passcode length must be 6-30 characters.
169
+
170
+ - **Scopes:** `spark-admin:telephony_config_write`
171
+ - **Note:** Uses a different URL pattern: `telephony/config/people/{person_id}/voicemail/passcode`
172
+
173
+ #### `reset_pin`
174
+
175
+ ```python
176
+ VoicemailApi.reset_pin(entity_id: str, org_id: str = None)
177
+ ```
178
+
179
+ Reset the voicemail PIN. Endpoint: `actions/resetPin/invoke` (POST).
180
+
181
+ - **Scopes:** `spark-admin:people_write`
182
+
183
+ ### Usage Example (from `examples/modify_voicemail.py`)
184
+
185
+ ```python
186
+ from wxc_sdk import WebexSimpleApi
187
+
188
+ api = WebexSimpleApi()
189
+ vm = api.person_settings.voicemail
190
+
191
+ # Read current settings
192
+ vm_settings = vm.read(person_id=person_id)
193
+
194
+ # Modify number of rings
195
+ vm_settings.send_unanswered_calls.number_of_rings = 6
196
+
197
+ # Write back
198
+ vm.configure(person_id, settings=vm_settings)
199
+ ```
200
+
201
+ The example script reads a CSV of usernames, filters to calling users, and bulk-updates ring count using `ThreadPoolExecutor`.
202
+
203
+ ### CLI Examples
204
+
205
+ ```bash
206
+ # Read voicemail settings for a person
207
+ wxcli user-settings show-voicemail PERSON_ID
208
+
209
+ # Read voicemail settings as JSON
210
+ wxcli user-settings show-voicemail PERSON_ID -o json
211
+
212
+ # Enable voicemail for a person
213
+ wxcli user-settings update-voicemail PERSON_ID --enabled
214
+
215
+ # Disable voicemail for a person
216
+ wxcli user-settings update-voicemail PERSON_ID --no-enabled
217
+
218
+ # Update voicemail with full settings (nested objects require --json-body)
219
+ wxcli user-settings update-voicemail PERSON_ID --json-body '{
220
+ "enabled": true,
221
+ "sendAllCalls": {"enabled": false},
222
+ "sendBusyCalls": {"enabled": false, "greeting": "DEFAULT"},
223
+ "sendUnansweredCalls": {"enabled": true, "greeting": "DEFAULT", "numberOfRings": 6},
224
+ "notifications": {"enabled": false},
225
+ "transferToNumber": {"enabled": false},
226
+ "emailCopyOfMessage": {"enabled": false},
227
+ "messageStorage": {"mwiEnabled": true, "storageType": "INTERNAL"},
228
+ "faxMessage": {"enabled": false}
229
+ }'
230
+
231
+ # Upload a custom busy voicemail greeting
232
+ wxcli user-settings configure-busy-voicemail PERSON_ID
233
+
234
+ # Upload a custom no-answer voicemail greeting
235
+ wxcli user-settings configure-no-answer PERSON_ID
236
+
237
+ # Reset voicemail PIN
238
+ wxcli user-settings reset-voicemail-pin PERSON_ID
239
+
240
+ # Set voicemail passcode
241
+ wxcli user-settings update-passcode PERSON_ID --passcode "123456"
242
+ ```
243
+
244
+ ### Raw HTTP
245
+
246
+ #### Read Voicemail Settings
247
+
248
+ ```
249
+ GET https://webexapis.com/v1/people/{personId}/features/voicemail
250
+ ```
251
+
252
+ ```python
253
+ result = api.session.rest_get(f"{BASE}/people/{person_id}/features/voicemail")
254
+ # Response:
255
+ # {
256
+ # "enabled": true,
257
+ # "sendAllCalls": {"enabled": false},
258
+ # "sendBusyCalls": {"enabled": false, "greeting": "DEFAULT", "greetingUploaded": false},
259
+ # "sendUnansweredCalls": {"enabled": true, "greeting": "DEFAULT", "numberOfRings": 3, "systemMaxNumberOfRings": 20},
260
+ # "notifications": {"enabled": false, "destination": ""},
261
+ # "transferToNumber": {"enabled": false, "destination": ""},
262
+ # "emailCopyOfMessage": {"enabled": false, "emailId": ""},
263
+ # "messageStorage": {"mwiEnabled": true, "storageType": "INTERNAL"},
264
+ # "faxMessage": {"enabled": false},
265
+ # "voiceMessageForwardingEnabled": false
266
+ # }
267
+ ```
268
+
269
+ #### Configure Voicemail Settings
270
+
271
+ ```
272
+ PUT https://webexapis.com/v1/people/{personId}/features/voicemail
273
+ ```
274
+
275
+ ```python
276
+ body = {
277
+ "enabled": True,
278
+ "sendAllCalls": {"enabled": False},
279
+ "sendBusyCalls": {"enabled": False, "greeting": "DEFAULT"},
280
+ "sendUnansweredCalls": {"enabled": True, "greeting": "DEFAULT", "numberOfRings": 6},
281
+ "notifications": {"enabled": False},
282
+ "transferToNumber": {"enabled": False},
283
+ "emailCopyOfMessage": {"enabled": False},
284
+ "messageStorage": {"mwiEnabled": True, "storageType": "INTERNAL"},
285
+ "faxMessage": {"enabled": False}
286
+ }
287
+ api.session.rest_put(f"{BASE}/people/{person_id}/features/voicemail", json=body)
288
+ ```
289
+
290
+ #### Upload Busy Greeting
291
+
292
+ ```
293
+ POST https://webexapis.com/v1/people/{personId}/features/voicemail/actions/uploadBusyGreeting/invoke
294
+ ```
295
+
296
+ Multipart/form-data with `.wav` file. Uses `rest_post` with file upload.
297
+
298
+ #### Upload No-Answer Greeting
299
+
300
+ ```
301
+ POST https://webexapis.com/v1/people/{personId}/features/voicemail/actions/uploadNoAnswerGreeting/invoke
302
+ ```
303
+
304
+ Multipart/form-data with `.wav` file. Uses `rest_post` with file upload.
305
+
306
+ #### Reset Voicemail PIN
307
+
308
+ ```
309
+ POST https://webexapis.com/v1/people/{personId}/features/voicemail/actions/resetPin/invoke
310
+ ```
311
+
312
+ ```python
313
+ api.session.rest_post(f"{BASE}/people/{person_id}/features/voicemail/actions/resetPin/invoke")
314
+ ```
315
+
316
+ #### Modify Voicemail Passcode
317
+
318
+ ```
319
+ PUT https://webexapis.com/v1/telephony/config/people/{personId}/voicemail/passcode
320
+ ```
321
+
322
+ ```python
323
+ body = {"passcode": "123456"}
324
+ api.session.rest_put(f"{BASE}/telephony/config/people/{person_id}/voicemail/passcode", json=body)
325
+ ```
326
+
327
+ **Gotchas:**
328
+ - Exclude read-only fields from PUT body: `greetingUploaded`, `systemMaxNumberOfRings`, `voiceMessageForwardingEnabled`.
329
+ - Passcode endpoint uses `telephony/config/people` path (not `people/{id}/features`). Requires `spark-admin:telephony_config_write` scope.
330
+ - Greeting uploads use POST with multipart/form-data, not JSON.
331
+
332
+ ---
333
+
334
+ ## 2. Caller ID
335
+
336
+ **API class:** `CallerIdApi`
337
+ **Feature key:** `callerId`
338
+ **Source:** `wxc_sdk/person_settings/caller_id.py`
339
+
340
+ ### Data Models
341
+
342
+ #### `CallerIdSelectedType` (Enum)
343
+
344
+ | Value | Description |
345
+ |-------|-------------|
346
+ | `DIRECT_LINE` | Show the caller's direct line number and/or extension |
347
+ | `LOCATION_NUMBER` | Show the main number for the location |
348
+ | `MOBILE_NUMBER` | Show the mobile number for this person |
349
+ | `CUSTOM` | Show the value from `custom_number` |
350
+
351
+ #### `ExternalCallerIdNamePolicy` (Enum)
352
+
353
+ | Value | Description |
354
+ |-------|-------------|
355
+ | `DIRECT_LINE` | Show the caller's direct line name |
356
+ | `LOCATION` | Show the site name for the location |
357
+ | `OTHER` | Show the value from `custom_external_caller_id_name` |
358
+
359
+ #### `CallerId`
360
+
361
+ | Field | Type | Description |
362
+ |-------|------|-------------|
363
+ | `caller_id_types` | `list[CallerIdSelectedType]` | Read-only. Allowed types for `selected`. (JSON alias: `types`) |
364
+ | `selected` | `CallerIdSelectedType` | Which type of outgoing caller ID is used (number portion) |
365
+ | `direct_number` | `str` | Direct line number (shown if `DIRECT_LINE` selected) |
366
+ | `extension_number` | `str` | Extension number (shown if `DIRECT_LINE` selected) |
367
+ | `location_number` | `str` | Location number (shown if `LOCATION_NUMBER` selected) |
368
+ | `mobile_number` | `str` | Mobile number (shown if `MOBILE_NUMBER` selected) |
369
+ | `toll_free_location_number` | `bool` | Indicates if the location number is toll-free |
370
+ | `custom_number` | `str` | Custom number (shown if `CUSTOM` selected). Must be from the entity's location or a matching location. |
371
+ | `first_name` | `str` | **Deprecated.** Use `direct_line_caller_id_name` and `dial_by_first_name` instead. |
372
+ | `last_name` | `str` | **Deprecated.** Use `direct_line_caller_id_name` and `dial_by_last_name` instead. |
373
+ | `block_in_forward_calls_enabled` | `bool` | Block identity when receiving transferred/forwarded calls |
374
+ | `external_caller_id_name_policy` | `ExternalCallerIdNamePolicy` | Which external caller ID name policy is used |
375
+ | `custom_external_caller_id_name` | `str` | Custom name shown when policy is `OTHER` |
376
+ | `location_external_caller_id_name` | `str` | Read-only. Location's caller ID name. |
377
+ | `additional_external_caller_id_direct_line_enabled` | `bool` | Set user's main number as additional external caller ID |
378
+ | `additional_external_caller_id_location_number_enabled` | `bool` | Set location main number as additional external caller ID |
379
+ | `additional_external_caller_id_custom_number` | `str` | Set any number across location as additional external caller ID |
380
+ | `display_name` | `str` | **Deprecated.** Use `direct_line_caller_id_name` and `dial_by_name` instead. |
381
+ | `display_detail` | `str` | **Deprecated.** Use `direct_line_caller_id_name` and `dial_by_name` instead. |
382
+ | `direct_line_caller_id_name` | `DirectLineCallerIdName` | Settings for direct line caller ID name |
383
+ | `dial_by_name` | `str` | Name used for dial-by-name functions |
384
+ | `dial_by_first_name` | `str` | First name for dial-by-name |
385
+ | `dial_by_last_name` | `str` | Last name for dial-by-name |
386
+
387
+ **Note:** Number fields (`direct_number`, `location_number`, `mobile_number`, `custom_number`) are validated through the `plus1` E.164 normalizer.
388
+
389
+ ### Methods
390
+
391
+ #### `read`
392
+
393
+ ```python
394
+ CallerIdApi.read(entity_id: str, org_id: str = None) -> CallerId
395
+ ```
396
+
397
+ - **Scopes:** `spark-admin:people_read` or `spark:people_read` (self)
398
+
399
+ #### `configure` (parameter-based)
400
+
401
+ ```python
402
+ CallerIdApi.configure(
403
+ entity_id: str,
404
+ org_id: str = None,
405
+ selected: CallerIdSelectedType = None,
406
+ custom_number: str = None,
407
+ first_name: str = None,
408
+ last_name: str = None,
409
+ external_caller_id_name_policy: ExternalCallerIdNamePolicy = None,
410
+ custom_external_caller_id_name: str = None,
411
+ )
412
+ ```
413
+
414
+ Update caller ID using individual parameters. Only non-None parameters are sent.
415
+
416
+ - **Scopes:** `spark-admin:people_write` or `spark:people_write` (self)
417
+
418
+ #### `configure_settings` (object-based)
419
+
420
+ ```python
421
+ CallerIdApi.configure_settings(entity_id: str, settings: CallerId, org_id: str = None)
422
+ ```
423
+
424
+ Update caller ID by passing a full `CallerId` object. Uses `settings.update()` which limits to `fields_for_update` and excludes read-only fields.
425
+
426
+ ```python
427
+ # Example: enable block-in-forward-calls
428
+ caller_id_settings = api.person_settings.caller_id.read(entity_id=person_id)
429
+ caller_id_settings.block_in_forward_calls_enabled = True
430
+ api.person_settings.caller_id.configure_settings(entity_id=person_id, settings=caller_id_settings)
431
+ ```
432
+
433
+ ### CLI Examples
434
+
435
+ ```bash
436
+ # Read caller ID settings for a person
437
+ wxcli user-settings list PERSON_ID
438
+
439
+ # Read caller ID settings as JSON
440
+ wxcli user-settings list PERSON_ID -o json
441
+
442
+ # Set caller ID to direct line
443
+ wxcli user-settings update-caller-id-features PERSON_ID --selected DIRECT_LINE
444
+
445
+ # Set caller ID to location number
446
+ wxcli user-settings update-caller-id-features PERSON_ID --selected LOCATION_NUMBER
447
+
448
+ # Set caller ID to a custom number
449
+ wxcli user-settings update-caller-id-features PERSON_ID --selected CUSTOM --custom-number "+12125559999"
450
+
451
+ # Set external caller ID name policy to location name
452
+ wxcli user-settings update-caller-id-features PERSON_ID --external-caller-id-name-policy LOCATION
453
+
454
+ # Set external caller ID name policy to custom name
455
+ wxcli user-settings update-caller-id-features PERSON_ID \
456
+ --external-caller-id-name-policy OTHER \
457
+ --custom-external-caller-id-name "Sales Department"
458
+
459
+ # Block identity when receiving forwarded calls
460
+ wxcli user-settings update-caller-id-features PERSON_ID --block-in-forward-calls-enabled
461
+
462
+ # Set dial-by-name fields
463
+ wxcli user-settings update-caller-id-features PERSON_ID \
464
+ --dial-by-first-name "Jane" --dial-by-last-name "Doe"
465
+ ```
466
+
467
+ ### Raw HTTP
468
+
469
+ #### Read Caller ID Settings
470
+
471
+ ```
472
+ GET https://webexapis.com/v1/people/{personId}/features/callerId
473
+ ```
474
+
475
+ ```python
476
+ result = api.session.rest_get(f"{BASE}/people/{person_id}/features/callerId")
477
+ # Response:
478
+ # {
479
+ # "types": ["DIRECT_LINE", "LOCATION_NUMBER", "CUSTOM"],
480
+ # "selected": "DIRECT_LINE",
481
+ # "directNumber": "+12125551234",
482
+ # "extensionNumber": "1234",
483
+ # "locationNumber": "+12125550000",
484
+ # "blockInForwardCallsEnabled": false,
485
+ # "externalCallerIdNamePolicy": "DIRECT_LINE",
486
+ # "locationExternalCallerIdName": "Main Office",
487
+ # "additionalExternalCallerIdDirectLineEnabled": false,
488
+ # "additionalExternalCallerIdLocationNumberEnabled": false,
489
+ # "dialByFirstName": "Jane",
490
+ # "dialByLastName": "Doe"
491
+ # }
492
+ ```
493
+
494
+ #### Configure Caller ID Settings
495
+
496
+ ```
497
+ PUT https://webexapis.com/v1/people/{personId}/features/callerId
498
+ ```
499
+
500
+ ```python
501
+ body = {
502
+ "selected": "DIRECT_LINE",
503
+ "blockInForwardCallsEnabled": True,
504
+ "externalCallerIdNamePolicy": "DIRECT_LINE"
505
+ }
506
+ api.session.rest_put(f"{BASE}/people/{person_id}/features/callerId", json=body)
507
+ ```
508
+
509
+ **Gotchas:**
510
+ - `types` is read-only (returned as JSON key `types`); do not include in PUT body.
511
+ - `locationExternalCallerIdName` is read-only.
512
+ - `selected` values: `"DIRECT_LINE"`, `"LOCATION_NUMBER"`, `"MOBILE_NUMBER"`, `"CUSTOM"`.
513
+ - `externalCallerIdNamePolicy` values: `"DIRECT_LINE"`, `"LOCATION"`, `"OTHER"`.
514
+ - When `selected` is `"CUSTOM"`, include `customNumber` in the body.
515
+
516
+ ---
517
+
518
+ ## 3. Agent Caller ID
519
+
520
+ **API class:** `AgentCallerIdApi`
521
+ **Feature key:** `agent`
522
+ **Source:** `wxc_sdk/person_settings/agent_caller_id.py`
523
+
524
+ Allows agents to set their outgoing caller ID to a call queue or hunt group instead of their personal caller ID.
525
+
526
+ ### Data Models
527
+
528
+ #### `AvailableCallerIdType` (Enum)
529
+
530
+ | Value | Description |
531
+ |-------|-------------|
532
+ | `CALL_QUEUE` | A call queue has been selected |
533
+ | `HUNT_GROUP` | A hunt group has been selected |
534
+
535
+ #### `AgentCallerId`
536
+
537
+ | Field | Type | Description |
538
+ |-------|------|-------------|
539
+ | `id` | `str` | Call queue or hunt group unique identifier |
540
+ | `type` | `AvailableCallerIdType` | `CALL_QUEUE` or `HUNT_GROUP` |
541
+ | `name` | `str` | Call queue or hunt group name |
542
+ | `phone_number` | `str` | Phone number (if set) |
543
+ | `extension` | `str` | Extension number (if set) |
544
+
545
+ #### `QueueCallerId`
546
+
547
+ Internal model used for the read response. Contains:
548
+
549
+ | Field | Type | Description |
550
+ |-------|------|-------------|
551
+ | `queue_caller_id_enabled` | `bool` | `true` when using the selected queue for caller ID; `false` for agent's own |
552
+ | `selected_queue` | `AgentCallerId` | The selected queue/hunt group (empty when disabled) |
553
+
554
+ ### Methods
555
+
556
+ #### `available_caller_ids`
557
+
558
+ ```python
559
+ AgentCallerIdApi.available_caller_ids(entity_id: str, org_id: str = None) -> list[AgentCallerId]
560
+ ```
561
+
562
+ Get call queues and hunt groups available for caller ID use by this agent.
563
+
564
+ - **Scopes:** `spark-admin:people_read`
565
+ - **Endpoint:** `telephony/config/people/{entity_id}/agent/availableCallerIds`
566
+
567
+ #### `read`
568
+
569
+ ```python
570
+ AgentCallerIdApi.read(entity_id: str) -> AgentCallerId
571
+ ```
572
+
573
+ Retrieve the agent's currently selected caller ID.
574
+
575
+ - **Scopes:** `spark-admin:telephony_config_read`
576
+ - **Endpoint:** `telephony/config/people/{entity_id}/agent/callerId`
577
+
578
+ #### `configure`
579
+
580
+ ```python
581
+ AgentCallerIdApi.configure(entity_id: str, selected_caller_id: str = None)
582
+ ```
583
+
584
+ Set the agent's caller ID to a specific call queue or hunt group. Pass `None` to revert to the agent's own caller ID.
585
+
586
+ - **Scopes:** `spark-admin:telephony_config_write`
587
+ - Body: `{"selectedCallerId": "<id_or_null>"}`
588
+
589
+ ### CLI Examples
590
+
591
+ ```bash
592
+ # List available caller IDs (call queues/hunt groups) for an agent
593
+ wxcli user-settings list-available-caller-ids PERSON_ID
594
+
595
+ # Read the agent's currently selected caller ID
596
+ wxcli user-settings show-caller-id PERSON_ID
597
+
598
+ # Read agent caller ID as JSON
599
+ wxcli user-settings show-caller-id PERSON_ID -o json
600
+
601
+ # Set agent caller ID to a specific call queue
602
+ wxcli user-settings update-caller-id-agent PERSON_ID --selected-caller-id QUEUE_ID
603
+
604
+ # Revert agent caller ID to the agent's own caller ID
605
+ wxcli user-settings update-caller-id-agent PERSON_ID --json-body '{"selectedCallerId": null}'
606
+ ```
607
+
608
+ ### Raw HTTP
609
+
610
+ > **Note:** Agent Caller ID uses the `telephony/config/people` path, not `people/{id}/features`.
611
+
612
+ #### List Available Agent Caller IDs
613
+
614
+ ```
615
+ GET https://webexapis.com/v1/telephony/config/people/{personId}/agent/availableCallerIds
616
+ ```
617
+
618
+ ```python
619
+ result = api.session.rest_get(f"{BASE}/telephony/config/people/{person_id}/agent/availableCallerIds")
620
+ # Response:
621
+ # {
622
+ # "availableCallerIds": [
623
+ # {"id": "queue-id", "type": "CALL_QUEUE", "name": "Sales Queue", "phoneNumber": "+12125551234", "extension": "8001"},
624
+ # {"id": "hg-id", "type": "HUNT_GROUP", "name": "Support HG", "extension": "8002"}
625
+ # ]
626
+ # }
627
+ ```
628
+
629
+ #### Read Agent Caller ID
630
+
631
+ ```
632
+ GET https://webexapis.com/v1/telephony/config/people/{personId}/agent/callerId
633
+ ```
634
+
635
+ ```python
636
+ result = api.session.rest_get(f"{BASE}/telephony/config/people/{person_id}/agent/callerId")
637
+ # Response:
638
+ # {
639
+ # "queueCallerIdEnabled": true,
640
+ # "selectedQueue": {"id": "queue-id", "type": "CALL_QUEUE", "name": "Sales Queue", ...}
641
+ # }
642
+ ```
643
+
644
+ #### Configure Agent Caller ID
645
+
646
+ ```
647
+ PUT https://webexapis.com/v1/telephony/config/people/{personId}/agent/callerId
648
+ ```
649
+
650
+ ```python
651
+ body = {"selectedCallerId": "queue-id"}
652
+ api.session.rest_put(f"{BASE}/telephony/config/people/{person_id}/agent/callerId", json=body)
653
+
654
+ # To revert to the agent's own caller ID:
655
+ body = {"selectedCallerId": None}
656
+ api.session.rest_put(f"{BASE}/telephony/config/people/{person_id}/agent/callerId", json=body)
657
+ ```
658
+
659
+ **Gotchas:**
660
+ - Uses `telephony_config_read`/`write` scopes (not `people_read`/`write`).
661
+ - Pass `null` for `selectedCallerId` to revert to agent's own caller ID.
662
+
663
+ ---
664
+
665
+ ## 4. Anonymous Call Rejection
666
+
667
+ **API class:** `AnonCallsApi`
668
+ **Feature key:** `anonymousCallReject`
669
+ **Source:** `wxc_sdk/person_settings/anon_calls.py`
670
+
671
+ When enabled, blocks all incoming calls from unidentified or blocked caller IDs.
672
+
673
+ > **Note from source:** This API is documented as "only available for professional licensed workspaces," and live testing confirms this. The OpenAPI spec only defines endpoints for `/me` (self) and `/workspaces/{id}` -- no admin-level `/people/{id}` endpoint exists. Attempting `GET /v1/telephony/config/people/{personId}/anonymousCallReject` returns 404. The workspace endpoint requires a Professional-licensed workspace (error 25409 returned for non-professional workspaces). The SDK's `PersonSettingsApiChild` base class constructs the person URL pattern, but the API does not accept it.
674
+
675
+ ### Methods
676
+
677
+ #### `read`
678
+
679
+ ```python
680
+ AnonCallsApi.read(entity_id: str, org_id: str = None) -> bool
681
+ ```
682
+
683
+ Returns a simple boolean: `True` if anonymous call rejection is enabled.
684
+
685
+ - **Scopes:** `spark-admin:workspaces_read` (workspace admin), `spark:people_read` (self via `/me`). No admin-level person endpoint exists.
686
+
687
+ #### `configure`
688
+
689
+ ```python
690
+ AnonCallsApi.configure(entity_id: str, enabled: bool, org_id: str = None)
691
+ ```
692
+
693
+ Enable or disable anonymous call rejection.
694
+
695
+ - Body: `{"enabled": true|false}`
696
+
697
+ ### CLI Examples
698
+
699
+ ```bash
700
+ # Read anonymous call rejection settings for a workspace
701
+ # Note: In wxcli, anonymous call rejection is under workspace-settings, not user-settings.
702
+ # The API is documented as "only available for professional licensed workspaces."
703
+ wxcli workspace-settings show-anonymous-call-reject WORKSPACE_ID
704
+
705
+ # Enable anonymous call rejection for a workspace
706
+ wxcli workspace-settings update-anonymous-call-reject WORKSPACE_ID --enabled
707
+
708
+ # Disable anonymous call rejection for a workspace
709
+ wxcli workspace-settings update-anonymous-call-reject WORKSPACE_ID --no-enabled
710
+ ```
711
+
712
+ ### Raw HTTP
713
+
714
+ > **WARNING: No admin-level person endpoint exists.** The path `people/{personId}/features/anonymousCallReject` returns 404. Only the workspace admin path and the self-service `/me` path work.
715
+
716
+ #### Read Anonymous Call Rejection (Workspace — admin token)
717
+
718
+ ```
719
+ GET https://webexapis.com/v1/telephony/config/workspaces/{workspaceId}/anonymousCallReject
720
+ ```
721
+
722
+ ```python
723
+ result = api.session.rest_get(f"{BASE}/telephony/config/workspaces/{workspace_id}/anonymousCallReject")
724
+ # Response:
725
+ # {
726
+ # "enabled": true
727
+ # }
728
+ ```
729
+
730
+ #### Read Anonymous Call Rejection (Self — user token, calling-licensed user)
731
+
732
+ ```
733
+ GET https://webexapis.com/v1/telephony/config/people/me/settings/anonymousCallReject
734
+ ```
735
+
736
+ ```python
737
+ # Requires calling-licensed user token with spark:telephony_config_read scope
738
+ result = api.session.rest_get(f"{BASE}/telephony/config/people/me/settings/anonymousCallReject")
739
+ # Response:
740
+ # {
741
+ # "enabled": true
742
+ # }
743
+ ```
744
+
745
+ #### Configure Anonymous Call Rejection (Workspace — admin token)
746
+
747
+ ```
748
+ PUT https://webexapis.com/v1/telephony/config/workspaces/{workspaceId}/anonymousCallReject
749
+ ```
750
+
751
+ ```python
752
+ body = {"enabled": True}
753
+ api.session.rest_put(f"{BASE}/telephony/config/workspaces/{workspace_id}/anonymousCallReject", json=body)
754
+ ```
755
+
756
+ #### Configure Anonymous Call Rejection (Self — user token, calling-licensed user)
757
+
758
+ ```
759
+ PUT https://webexapis.com/v1/telephony/config/people/me/settings/anonymousCallReject
760
+ ```
761
+
762
+ ```python
763
+ # Requires calling-licensed user token with spark:telephony_config_write scope
764
+ body = {"enabled": True}
765
+ api.session.rest_put(f"{BASE}/telephony/config/people/me/settings/anonymousCallReject", json=body)
766
+ ```
767
+
768
+ ---
769
+
770
+ ## 5. Privacy
771
+
772
+ **API class:** `PrivacyApi`
773
+ **Feature key:** `privacy`
774
+ **Source:** `wxc_sdk/person_settings/privacy.py`
775
+
776
+ Controls whether the entity's line can be monitored by others and whether they are reachable via Auto Attendant services.
777
+
778
+ ### Data Model
779
+
780
+ #### `Privacy`
781
+
782
+ | Field | Type | Description |
783
+ |-------|------|-------------|
784
+ | `aa_extension_dialing_enabled` | `bool` | Enable auto attendant extension dialing |
785
+ | `aa_naming_dialing_enabled` | `bool` | Enable auto attendant dialing by first or last name |
786
+ | `enable_phone_status_directory_privacy` | `bool` | Enable phone status directory privacy |
787
+ | `enable_phone_status_pickup_barge_in_privacy` | `bool` | When `true`, only people in `monitoring_agents` can pick up the call or barge in by dialing the extension |
788
+ | `monitoring_agents` | `list[Union[str, PersonPlaceAgent]]` | List of people allowed to monitor. For updates, can pass IDs (strings) directly. |
789
+
790
+ ### Methods
791
+
792
+ #### `read`
793
+
794
+ ```python
795
+ PrivacyApi.read(entity_id: str, org_id: str = None) -> Privacy
796
+ ```
797
+
798
+ - **Scopes:** `spark-admin:people_read`
799
+
800
+ #### `configure`
801
+
802
+ ```python
803
+ PrivacyApi.configure(entity_id: str, settings: Privacy, org_id: str = None)
804
+ ```
805
+
806
+ - **Scopes:** `spark-admin:people_write`
807
+ - When updating `monitoring_agents`, the SDK extracts IDs from `PersonPlaceAgent` objects (using `agent_id`) or passes strings directly.
808
+
809
+ ### CLI Examples
810
+
811
+ ```bash
812
+ # Read privacy settings for a person
813
+ wxcli user-settings list-privacy PERSON_ID
814
+
815
+ # Read privacy settings as JSON
816
+ wxcli user-settings list-privacy PERSON_ID -o json
817
+
818
+ # Enable AA extension dialing and directory privacy
819
+ wxcli user-settings update-privacy PERSON_ID \
820
+ --aa-extension-dialing-enabled \
821
+ --enable-phone-status-directory-privacy
822
+
823
+ # Disable AA naming dialing
824
+ wxcli user-settings update-privacy PERSON_ID --no-aa-naming-dialing-enabled
825
+
826
+ # Enable pickup/barge-in privacy
827
+ wxcli user-settings update-privacy PERSON_ID --enable-phone-status-pickup-barge-in-privacy
828
+
829
+ # Update privacy with monitoring agents list (requires --json-body)
830
+ wxcli user-settings update-privacy PERSON_ID --json-body '{
831
+ "aaExtensionDialingEnabled": true,
832
+ "aaNamingDialingEnabled": true,
833
+ "enablePhoneStatusDirectoryPrivacy": true,
834
+ "enablePhoneStatusPickupBargeInPrivacy": true,
835
+ "monitoringAgents": ["PERSON_ID_1", "PERSON_ID_2"]
836
+ }'
837
+ ```
838
+
839
+ ### Raw HTTP
840
+
841
+ #### Read Privacy Settings
842
+
843
+ ```
844
+ GET https://webexapis.com/v1/people/{personId}/features/privacy
845
+ ```
846
+
847
+ ```python
848
+ result = api.session.rest_get(f"{BASE}/people/{person_id}/features/privacy")
849
+ # Response:
850
+ # {
851
+ # "aaExtensionDialingEnabled": true,
852
+ # "aaNamingDialingEnabled": true,
853
+ # "enablePhoneStatusDirectoryPrivacy": false,
854
+ # "enablePhoneStatusPickupBargeInPrivacy": false,
855
+ # "monitoringAgents": [
856
+ # {"id": "person-id", "firstName": "Jane", "lastName": "Doe", ...}
857
+ # ]
858
+ # }
859
+ ```
860
+
861
+ #### Configure Privacy Settings
862
+
863
+ ```
864
+ PUT https://webexapis.com/v1/people/{personId}/features/privacy
865
+ ```
866
+
867
+ ```python
868
+ body = {
869
+ "aaExtensionDialingEnabled": True,
870
+ "aaNamingDialingEnabled": True,
871
+ "enablePhoneStatusDirectoryPrivacy": True,
872
+ "enablePhoneStatusPickupBargeInPrivacy": False,
873
+ "monitoringAgents": ["person-id-1", "person-id-2"]
874
+ }
875
+ api.session.rest_put(f"{BASE}/people/{person_id}/features/privacy", json=body)
876
+ ```
877
+
878
+ **Gotchas:**
879
+ - `monitoringAgents` accepts a flat list of person IDs for updates, even though reads return full agent objects.
880
+ - Uses `people/{id}/features/privacy` path (not `telephony/config/people`). <!-- Updated by playbook session 2026-03-18 -->
881
+
882
+ ---
883
+
884
+ ## 6. Barge-In
885
+
886
+ **API class:** `BargeApi`
887
+ **Feature key:** `bargeIn`
888
+ **Source:** `wxc_sdk/person_settings/barge.py`
889
+
890
+ Enables the use of a Feature Access Code (FAC) to answer a call directed to another subscriber, or barge-in on an already-answered call. Works across locations.
891
+
892
+ ### Data Model
893
+
894
+ #### `BargeSettings`
895
+
896
+ | Field | Type | Description |
897
+ |-------|------|-------------|
898
+ | `enabled` | `bool` | Whether barge-in is enabled (required, not optional) |
899
+ | `tone_enabled` | `bool` | Whether a stutter dial tone plays when someone barges in (required, not optional) |
900
+
901
+ ### Methods
902
+
903
+ #### `read`
904
+
905
+ ```python
906
+ BargeApi.read(entity_id: str, org_id: str = None) -> BargeSettings
907
+ ```
908
+
909
+ - **Scopes:** `spark-admin:people_read` or `spark:people_read` (self)
910
+
911
+ #### `configure`
912
+
913
+ ```python
914
+ BargeApi.configure(entity_id: str, barge_settings: BargeSettings, org_id: str = None)
915
+ ```
916
+
917
+ - **Scopes:** `spark-admin:people_write` or `spark:people_write` (self)
918
+ - Serializes the full `BargeSettings` object as JSON body.
919
+
920
+ ### CLI Examples
921
+
922
+ ```bash
923
+ # Read barge-in settings for a person
924
+ wxcli user-settings show-barge-in PERSON_ID
925
+
926
+ # Read barge-in settings as JSON
927
+ wxcli user-settings show-barge-in PERSON_ID -o json
928
+
929
+ # Enable barge-in with tone
930
+ wxcli user-settings update-barge-in PERSON_ID --enabled --tone-enabled
931
+
932
+ # Enable barge-in without tone
933
+ wxcli user-settings update-barge-in PERSON_ID --enabled --no-tone-enabled
934
+
935
+ # Disable barge-in entirely
936
+ wxcli user-settings update-barge-in PERSON_ID --no-enabled --no-tone-enabled
937
+ ```
938
+
939
+ ### Raw HTTP
940
+
941
+ #### Read Barge-In Settings
942
+
943
+ ```
944
+ GET https://webexapis.com/v1/people/{personId}/features/bargeIn
945
+ ```
946
+
947
+ ```python
948
+ result = api.session.rest_get(f"{BASE}/people/{person_id}/features/bargeIn")
949
+ # Response:
950
+ # {
951
+ # "enabled": true,
952
+ # "toneEnabled": true
953
+ # }
954
+ ```
955
+
956
+ #### Configure Barge-In Settings
957
+
958
+ ```
959
+ PUT https://webexapis.com/v1/people/{personId}/features/bargeIn
960
+ ```
961
+
962
+ ```python
963
+ body = {
964
+ "enabled": True,
965
+ "toneEnabled": True
966
+ }
967
+ api.session.rest_put(f"{BASE}/people/{person_id}/features/bargeIn", json=body)
968
+ ```
969
+
970
+ #### Beta: User Self-Service Barge-In
971
+
972
+ > **Beta API** — May change without notice.
973
+
974
+ Users can read/modify their own barge-in settings via user-level OAuth:
975
+
976
+ - `GET /telephony/config/people/me/settings/bargeIn`
977
+ - `PUT /telephony/config/people/me/settings/bargeIn`
978
+
979
+ The admin-path equivalent at `/people/{personId}/features/bargeIn` remains the stable, non-beta option for admin-managed configuration.
980
+
981
+ ---
982
+
983
+ ## 7. Call Recording
984
+
985
+ **API class:** `CallRecordingApi`
986
+ **Feature key:** `callRecording`
987
+ **Source:** `wxc_sdk/person_settings/call_recording.py`
988
+
989
+ Provides hosted call recording for replay and archival, for quality assurance, security, and training.
990
+
991
+ ### Data Models
992
+
993
+ #### `Record` (Enum)
994
+
995
+ | Value | Description |
996
+ |-------|-------------|
997
+ | `Always` | Recording is always enabled |
998
+ | `Never` | Recording is never enabled |
999
+ | `On Demand` | Recording is started/stopped manually by user |
1000
+ | `Always with Pause/Resume` | Always enabled with ability to pause and resume |
1001
+ | `On Demand with User Initiated Start` | Started manually by user |
1002
+
1003
+ #### `NotificationType` (Enum)
1004
+
1005
+ | Value | Description |
1006
+ |-------|-------------|
1007
+ | `None` | No notification sound when paused/resumed |
1008
+ | `Beep` | A beep sound plays when paused/resumed |
1009
+ | `Play Announcement` | A verbal announcement plays when paused/resumed |
1010
+
1011
+ #### `NotificationRepeat`
1012
+
1013
+ | Field | Type | Description |
1014
+ |-------|------|-------------|
1015
+ | `interval` | `int` | Interval in seconds (10-1800) for periodic warning beep |
1016
+ | `enabled` | `bool` | Whether the ongoing recording tone plays at the interval |
1017
+
1018
+ #### `Notification`
1019
+
1020
+ | Field | Type | Description |
1021
+ |-------|------|-------------|
1022
+ | `notification_type` | `NotificationType` | Type of notification (JSON alias: `type`) |
1023
+ | `enabled` | `bool` | Whether notification feature is active |
1024
+
1025
+ #### `StartStopAnnouncement`
1026
+
1027
+ | Field | Type | Description |
1028
+ |-------|------|-------------|
1029
+ | `internal_calls_enabled` | `bool` | Play start/stop announcement for internal calls |
1030
+ | `pstn_calls_enabled` | `bool` | Play start/stop announcement for PSTN calls |
1031
+
1032
+ #### `CallRecordingAccessSettings`
1033
+
1034
+ | Field | Type | Description |
1035
+ |-------|------|-------------|
1036
+ | `view_and_play_recordings_enabled` | `bool` | Person can view and play recordings |
1037
+ | `download_recordings_enabled` | `bool` | Person can download recordings |
1038
+ | `delete_recordings_enabled` | `bool` | Person can delete recordings |
1039
+ | `share_recordings_enabled` | `bool` | Person can share recordings |
1040
+
1041
+ #### `PostCallRecordingSettings`
1042
+
1043
+ | Field | Type | Description |
1044
+ |-------|------|-------------|
1045
+ | `summary_and_action_items_enabled` | `bool` | Undocumented field; present in SDK source (`call_recording.py` line 83) with `# TODO: undocumented, issue 201` |
1046
+ | `transcript_enabled` | `bool` | Undocumented field; present in SDK source (`call_recording.py` line 84) with `# TODO: undocumented, issue 201` |
1047
+
1048
+ #### `CallRecordingSetting`
1049
+
1050
+ | Field | Type | Description |
1051
+ |-------|------|-------------|
1052
+ | `enabled` | `bool` | Whether call recording is enabled |
1053
+ | `record` | `Record` | Which scenario triggers recording |
1054
+ | `record_voicemail_enabled` | `bool` | Also record voicemail messages |
1055
+ | `start_stop_announcement_enabled` | `bool` | Play announcement at start/stop |
1056
+ | `notification` | `Notification` | Pause/resume notification settings |
1057
+ | `repeat` | `NotificationRepeat` | Periodic beep settings |
1058
+ | `service_provider` | `str` | Read-only. Service provider name (excluded from updates). |
1059
+ | `external_group` | `str` | Read-only. Service provider group (excluded from updates). |
1060
+ | `external_identifier` | `str` | Read-only. Unique identifier at provider (excluded from updates). |
1061
+ | `start_stop_announcement` | `StartStopAnnouncement` | Separate start/stop per internal vs. PSTN |
1062
+ | `call_recording_access_settings` | `CallRecordingAccessSettings` | User's access to their own recordings |
1063
+ | `post_call_recording_settings` | `PostCallRecordingSettings` | Post-call summary/transcript settings |
1064
+
1065
+ #### Default Settings
1066
+
1067
+ ```python
1068
+ CallRecordingSetting.default() # Returns:
1069
+ CallRecordingSetting(
1070
+ enabled=False,
1071
+ record=Record.never,
1072
+ record_voicemail_enabled=False,
1073
+ start_stop_announcement_enabled=False,
1074
+ notification=Notification(notification_type=NotificationType.none, enabled=False),
1075
+ repeat=NotificationRepeat(interval=15, enabled=False),
1076
+ start_stop_announcement=StartStopAnnouncement(
1077
+ internal_calls_enabled=False,
1078
+ pstn_calls_enabled=False,
1079
+ ),
1080
+ )
1081
+ ```
1082
+
1083
+ ### Methods
1084
+
1085
+ #### `read`
1086
+
1087
+ ```python
1088
+ CallRecordingApi.read(entity_id: str, org_id: str = None) -> CallRecordingSetting
1089
+ ```
1090
+
1091
+ - **Scopes:** `spark-admin:people_read` (Note: the SDK source docstring incorrectly says `people_write` for read; this is a documentation bug in the SDK -- read operations use `people_read` per the consistent pattern across all PersonSettingsApiChild endpoints)
1092
+
1093
+ #### `configure`
1094
+
1095
+ ```python
1096
+ CallRecordingApi.configure(entity_id: str, recording: CallRecordingSetting, org_id: str = None)
1097
+ ```
1098
+
1099
+ - **Scopes:** `spark-admin:people_write`
1100
+ - **Gotcha:** When `notification.notification_type` is `None` (the enum value), the update method converts it to JSON `null` (the API returns the string `"None"` on reads but expects `null` on writes).
1101
+ - Read-only fields (`service_provider`, `external_group`, `external_identifier`) are automatically excluded.
1102
+
1103
+ ### CLI Examples
1104
+
1105
+ ```bash
1106
+ # Read call recording settings for a person
1107
+ wxcli user-settings show-call-recording PERSON_ID
1108
+
1109
+ # Read call recording settings as JSON
1110
+ wxcli user-settings show-call-recording PERSON_ID -o json
1111
+
1112
+ # Enable call recording with "Always" mode
1113
+ wxcli user-settings update-call-recording PERSON_ID --enabled --record "Always"
1114
+
1115
+ # Set call recording to "On Demand with User Initiated Start"
1116
+ wxcli user-settings update-call-recording PERSON_ID --enabled --record "On Demand with User Initiated Start"
1117
+
1118
+ # Disable call recording
1119
+ wxcli user-settings update-call-recording PERSON_ID --no-enabled --record "Never"
1120
+
1121
+ # Enable call recording with voicemail recording
1122
+ wxcli user-settings update-call-recording PERSON_ID --enabled --record "Always" --record-voicemail-enabled
1123
+
1124
+ # Full recording config with notifications (requires --json-body)
1125
+ wxcli user-settings update-call-recording PERSON_ID --json-body '{
1126
+ "enabled": true,
1127
+ "record": "Always with Pause/Resume",
1128
+ "recordVoicemailEnabled": false,
1129
+ "startStopAnnouncementEnabled": true,
1130
+ "notification": {"type": null, "enabled": false},
1131
+ "repeat": {"interval": 15, "enabled": false},
1132
+ "startStopAnnouncement": {"internalCallsEnabled": true, "pstnCallsEnabled": true}
1133
+ }'
1134
+ ```
1135
+
1136
+ ### Raw HTTP
1137
+
1138
+ > **Note:** The CLI uses `people/{id}/features/callRecording`. The user-provided URL pattern `telephony/config/people/{personId}/callRecording` is the wxc_sdk remapped path; the actual raw HTTP endpoint is `people/{id}/features/callRecording`. <!-- Updated by playbook session 2026-03-18 -->
1139
+
1140
+ #### Read Call Recording Settings
1141
+
1142
+ ```
1143
+ GET https://webexapis.com/v1/people/{personId}/features/callRecording
1144
+ ```
1145
+
1146
+ ```python
1147
+ result = api.session.rest_get(f"{BASE}/people/{person_id}/features/callRecording")
1148
+ # Response:
1149
+ # {
1150
+ # "enabled": false,
1151
+ # "record": "Never",
1152
+ # "recordVoicemailEnabled": false,
1153
+ # "startStopAnnouncementEnabled": false,
1154
+ # "notification": {"type": null, "enabled": false},
1155
+ # "repeat": {"interval": 15, "enabled": false},
1156
+ # "serviceProvider": "...",
1157
+ # "externalGroup": "...",
1158
+ # "externalIdentifier": "...",
1159
+ # "startStopAnnouncement": {"internalCallsEnabled": false, "pstnCallsEnabled": false},
1160
+ # "callRecordingAccessSettings": {
1161
+ # "viewAndPlayRecordingsEnabled": true,
1162
+ # "downloadRecordingsEnabled": true,
1163
+ # "deleteRecordingsEnabled": true,
1164
+ # "shareRecordingsEnabled": true
1165
+ # }
1166
+ # }
1167
+ ```
1168
+
1169
+ #### Configure Call Recording Settings
1170
+
1171
+ ```
1172
+ PUT https://webexapis.com/v1/people/{personId}/features/callRecording
1173
+ ```
1174
+
1175
+ ```python
1176
+ body = {
1177
+ "enabled": True,
1178
+ "record": "Always",
1179
+ "recordVoicemailEnabled": False,
1180
+ "startStopAnnouncementEnabled": False,
1181
+ "notification": {"type": None, "enabled": False},
1182
+ "repeat": {"interval": 15, "enabled": False},
1183
+ "startStopAnnouncement": {"internalCallsEnabled": False, "pstnCallsEnabled": False}
1184
+ }
1185
+ api.session.rest_put(f"{BASE}/people/{person_id}/features/callRecording", json=body)
1186
+ ```
1187
+
1188
+ **Gotchas:**
1189
+ - Exclude read-only fields from PUT body: `serviceProvider`, `externalGroup`, `externalIdentifier`.
1190
+ - `record` values: `"Always"`, `"Never"`, `"On Demand"`, `"Always with Pause/Resume"`, `"On Demand with User Initiated Start"`.
1191
+ - `notification.type`: API returns `"None"` as a string on reads but expects JSON `null` on writes. Pass `None` (Python) which serializes to `null`.
1192
+
1193
+ ---
1194
+
1195
+ ## 8. Call Intercept
1196
+
1197
+ **API class:** `CallInterceptApi`
1198
+ **Feature key:** `intercept`
1199
+ **Source:** `wxc_sdk/person_settings/call_intercept.py`
1200
+
1201
+ Gracefully takes an entity's phone out of service while providing callers with informative announcements and alternative routing options.
1202
+
1203
+ ### Data Models
1204
+
1205
+ #### `InterceptTypeIncoming` (Enum)
1206
+
1207
+ | Value | Description |
1208
+ |-------|-------------|
1209
+ | `INTERCEPT_ALL` | All incoming calls are intercepted |
1210
+ | `ALLOW_ALL` | Incoming calls are not intercepted |
1211
+
1212
+ #### `InterceptTypeOutgoing` (Enum)
1213
+
1214
+ | Value | Description |
1215
+ |-------|-------------|
1216
+ | `INTERCEPT_ALL` | All outgoing calls are intercepted |
1217
+ | `ALLOW_LOCAL_ONLY` | Only non-local calls are intercepted |
1218
+
1219
+ #### `InterceptNumber`
1220
+
1221
+ | Field | Type | Description |
1222
+ |-------|------|-------------|
1223
+ | `enabled` | `bool` | If true, caller hears this number announced |
1224
+ | `destination` | `str` | The number the caller hears |
1225
+
1226
+ #### `InterceptAnnouncements`
1227
+
1228
+ | Field | Type | Description |
1229
+ |-------|------|-------------|
1230
+ | `greeting` | `Greeting` | `DEFAULT` or `CUSTOM` |
1231
+ | `file_name` | `str` | Read-only. Filename of custom greeting (empty string if none uploaded). Excluded from updates. |
1232
+ | `new_number` | `InterceptNumber` | Announcement about a new number |
1233
+ | `zero_transfer` | `InterceptNumber` | How the call is handled if 0 is pressed |
1234
+
1235
+ #### `InterceptSettingIncoming`
1236
+
1237
+ | Field | Type | Description |
1238
+ |-------|------|-------------|
1239
+ | `intercept_type` | `InterceptTypeIncoming` | (JSON alias: `type`) |
1240
+ | `voicemail_enabled` | `bool` | If true, destination is voicemail |
1241
+ | `announcements` | `InterceptAnnouncements` | Announcement settings |
1242
+
1243
+ #### `InterceptSettingOutgoing`
1244
+
1245
+ | Field | Type | Description |
1246
+ |-------|------|-------------|
1247
+ | `intercept_type` | `InterceptTypeOutgoing` | (JSON alias: `type`) |
1248
+ | `transfer_enabled` | `bool` | Allow transfer/forwarding for intercepted calls |
1249
+ | `destination` | `str` | Number to transfer outbound calls to |
1250
+
1251
+ #### `InterceptSetting`
1252
+
1253
+ | Field | Type | Description |
1254
+ |-------|------|-------------|
1255
+ | `enabled` | `bool` | Whether call intercept is active |
1256
+ | `incoming` | `InterceptSettingIncoming` | Incoming call intercept configuration |
1257
+ | `outgoing` | `InterceptSettingOutgoing` | Outgoing call intercept configuration |
1258
+
1259
+ #### Default Settings
1260
+
1261
+ ```python
1262
+ InterceptSetting.default() # Returns:
1263
+ InterceptSetting(
1264
+ enabled=False,
1265
+ incoming=InterceptSettingIncoming(
1266
+ intercept_type=InterceptTypeIncoming.intercept_all,
1267
+ voicemail_enabled=False,
1268
+ announcements=InterceptAnnouncements(
1269
+ greeting=Greeting.default,
1270
+ new_number=InterceptNumber(enabled=False),
1271
+ zero_transfer=InterceptNumber(enabled=False),
1272
+ ),
1273
+ ),
1274
+ outgoing=InterceptSettingOutgoing(
1275
+ intercept_type=InterceptTypeOutgoing.intercept_all,
1276
+ transfer_enabled=False,
1277
+ ),
1278
+ )
1279
+ ```
1280
+
1281
+ ### Methods
1282
+
1283
+ #### `read`
1284
+
1285
+ ```python
1286
+ CallInterceptApi.read(entity_id: str, org_id: str = None) -> InterceptSetting
1287
+ ```
1288
+
1289
+ - **Scopes:** `spark-admin:people_read`
1290
+
1291
+ #### `configure`
1292
+
1293
+ ```python
1294
+ CallInterceptApi.configure(entity_id: str, intercept: InterceptSetting, org_id: str = None)
1295
+ ```
1296
+
1297
+ - **Scopes:** `spark-admin:people_write`
1298
+
1299
+ #### `greeting`
1300
+
1301
+ ```python
1302
+ CallInterceptApi.greeting(
1303
+ entity_id: str,
1304
+ content: Union[BufferedReader, str],
1305
+ upload_as: str = None,
1306
+ org_id: str = None,
1307
+ )
1308
+ ```
1309
+
1310
+ Upload a custom intercept greeting (`.wav` file). Uses multipart/form-data. Endpoint: `actions/announcementUpload/invoke`.
1311
+
1312
+ - `content`: file path (str) or open binary reader.
1313
+ - `upload_as`: required if `content` is a reader.
1314
+ - **Scopes:** `spark-admin:people_write` or `spark:people_write` (self)
1315
+
1316
+ ### CLI Examples
1317
+
1318
+ ```bash
1319
+ # Read call intercept settings for a person
1320
+ wxcli user-settings show-intercept PERSON_ID
1321
+
1322
+ # Read call intercept settings as JSON
1323
+ wxcli user-settings show-intercept PERSON_ID -o json
1324
+
1325
+ # Enable call intercept
1326
+ wxcli user-settings update-intercept PERSON_ID --enabled
1327
+
1328
+ # Disable call intercept
1329
+ wxcli user-settings update-intercept PERSON_ID --no-enabled
1330
+
1331
+ # Enable call intercept with full incoming/outgoing config (requires --json-body)
1332
+ wxcli user-settings update-intercept PERSON_ID --json-body '{
1333
+ "enabled": true,
1334
+ "incoming": {
1335
+ "type": "INTERCEPT_ALL",
1336
+ "voicemailEnabled": true,
1337
+ "announcements": {
1338
+ "greeting": "DEFAULT",
1339
+ "newNumber": {"enabled": true, "destination": "+12125559999"},
1340
+ "zeroTransfer": {"enabled": true, "destination": "+12125550000"}
1341
+ }
1342
+ },
1343
+ "outgoing": {
1344
+ "type": "ALLOW_LOCAL_ONLY",
1345
+ "transferEnabled": false
1346
+ }
1347
+ }'
1348
+
1349
+ # Upload a custom intercept greeting
1350
+ wxcli user-settings configure-call-intercept PERSON_ID
1351
+ ```
1352
+
1353
+ ### Raw HTTP
1354
+
1355
+ #### Read Call Intercept Settings
1356
+
1357
+ ```
1358
+ GET https://webexapis.com/v1/people/{personId}/features/intercept
1359
+ ```
1360
+
1361
+ ```python
1362
+ result = api.session.rest_get(f"{BASE}/people/{person_id}/features/intercept")
1363
+ # Response:
1364
+ # {
1365
+ # "enabled": false,
1366
+ # "incoming": {
1367
+ # "type": "INTERCEPT_ALL",
1368
+ # "voicemailEnabled": false,
1369
+ # "announcements": {
1370
+ # "greeting": "DEFAULT",
1371
+ # "fileName": "",
1372
+ # "newNumber": {"enabled": false, "destination": ""},
1373
+ # "zeroTransfer": {"enabled": false, "destination": ""}
1374
+ # }
1375
+ # },
1376
+ # "outgoing": {
1377
+ # "type": "INTERCEPT_ALL",
1378
+ # "transferEnabled": false,
1379
+ # "destination": ""
1380
+ # }
1381
+ # }
1382
+ ```
1383
+
1384
+ #### Configure Call Intercept Settings
1385
+
1386
+ ```
1387
+ PUT https://webexapis.com/v1/people/{personId}/features/intercept
1388
+ ```
1389
+
1390
+ ```python
1391
+ body = {
1392
+ "enabled": True,
1393
+ "incoming": {
1394
+ "type": "INTERCEPT_ALL",
1395
+ "voicemailEnabled": True,
1396
+ "announcements": {
1397
+ "greeting": "DEFAULT",
1398
+ "newNumber": {"enabled": False},
1399
+ "zeroTransfer": {"enabled": False}
1400
+ }
1401
+ },
1402
+ "outgoing": {
1403
+ "type": "INTERCEPT_ALL",
1404
+ "transferEnabled": False
1405
+ }
1406
+ }
1407
+ api.session.rest_put(f"{BASE}/people/{person_id}/features/intercept", json=body)
1408
+ ```
1409
+
1410
+ #### Upload Custom Intercept Greeting
1411
+
1412
+ ```
1413
+ POST https://webexapis.com/v1/people/{personId}/features/intercept/actions/announcementUpload/invoke
1414
+ ```
1415
+
1416
+ Multipart/form-data with `.wav` file. Uses `rest_post` with file upload.
1417
+
1418
+ **Gotchas:**
1419
+ - `incoming.announcements.fileName` is read-only; do not include in PUT body.
1420
+ - `incoming.type` values: `"INTERCEPT_ALL"`, `"ALLOW_ALL"`.
1421
+ - `outgoing.type` values: `"INTERCEPT_ALL"`, `"ALLOW_LOCAL_ONLY"`.
1422
+ - The JSON key for intercept type is `type` (not `interceptType`), matching the SDK's JSON alias.
1423
+
1424
+ ---
1425
+
1426
+ ## 9. Monitoring
1427
+
1428
+ **API class:** `MonitoringApi`
1429
+ **Feature key:** `monitoring`
1430
+ **Source:** `wxc_sdk/person_settings/monitoring.py`
1431
+
1432
+ Shows specified people, places, virtual lines, or call park extensions that are being monitored. Monitors line status (on-call, parked).
1433
+
1434
+ ### Data Models
1435
+
1436
+ #### `MonitoredElementMember` (extends `MonitoredMember`)
1437
+
1438
+ | Field | Type | Description |
1439
+ |-------|------|-------------|
1440
+ | (inherited fields) | | From `MonitoredMember` (member_id, first_name, last_name, etc.) |
1441
+ | `location_id` | `str` | The location ID for this monitored member |
1442
+
1443
+ Has a property `ci_location_id` that converts `location_id` to UUID format.
1444
+
1445
+ #### `MonitoredElement`
1446
+
1447
+ | Field | Type | Description |
1448
+ |-------|------|-------------|
1449
+ | `member` | `MonitoredElementMember` | Monitored person or place |
1450
+ | `cpe` | `CallParkExtension` | Monitored call park extension (JSON alias: `callparkextension`) |
1451
+
1452
+ #### `Monitoring`
1453
+
1454
+ | Field | Type | Description |
1455
+ |-------|------|-------------|
1456
+ | `call_park_notification_enabled` | `bool` | Enable/disable call park notifications |
1457
+ | `monitored_elements` | `list[Union[str, MonitoredElement]]` | Monitored elements (max 50). For updates, can pass IDs (strings) directly. |
1458
+
1459
+ **Convenience properties:**
1460
+ - `monitored_cpes` -> `list[CallParkExtension]` -- filters to just call park extensions
1461
+ - `monitored_members` -> `list[MonitoredElementMember]` -- filters to just members
1462
+
1463
+ ### Methods
1464
+
1465
+ #### `read`
1466
+
1467
+ ```python
1468
+ MonitoringApi.read(entity_id: str, org_id: str = None) -> Monitoring
1469
+ ```
1470
+
1471
+ - **Scopes:** `spark-admin:people_read`
1472
+
1473
+ #### `configure`
1474
+
1475
+ ```python
1476
+ MonitoringApi.configure(entity_id: str, settings: Monitoring, org_id: str = None)
1477
+ ```
1478
+
1479
+ - **Scopes:** `spark-admin:people_write`
1480
+ - **Max 50 elements.** The SDK extracts IDs from `MonitoredElement` objects (using `member.member_id` or `cpe.cpe_id`) or passes strings directly.
1481
+ - Body format:
1482
+ ```json
1483
+ {
1484
+ "enableCallParkNotification": true,
1485
+ "monitoredElements": ["id1", "id2", ...]
1486
+ }
1487
+ ```
1488
+
1489
+ ### CLI Examples
1490
+
1491
+ ```bash
1492
+ # Read monitoring settings for a person
1493
+ wxcli user-settings list-monitoring PERSON_ID
1494
+
1495
+ # Read monitoring settings as JSON
1496
+ wxcli user-settings list-monitoring PERSON_ID -o json
1497
+
1498
+ # Enable call park notification
1499
+ wxcli user-settings update-monitoring PERSON_ID --enable-call-park-notification
1500
+
1501
+ # Disable call park notification
1502
+ wxcli user-settings update-monitoring PERSON_ID --no-enable-call-park-notification
1503
+
1504
+ # Update monitored elements list (requires --json-body for element IDs)
1505
+ wxcli user-settings update-monitoring PERSON_ID --json-body '{
1506
+ "enableCallParkNotification": true,
1507
+ "monitoredElements": ["PERSON_ID_1", "PERSON_ID_2", "CPE_ID_1"]
1508
+ }'
1509
+ ```
1510
+
1511
+ ### Raw HTTP
1512
+
1513
+ #### Read Monitoring Settings
1514
+
1515
+ ```
1516
+ GET https://webexapis.com/v1/people/{personId}/features/monitoring
1517
+ ```
1518
+
1519
+ ```python
1520
+ result = api.session.rest_get(f"{BASE}/people/{person_id}/features/monitoring")
1521
+ # Response:
1522
+ # {
1523
+ # "enableCallParkNotification": true,
1524
+ # "monitoredElements": [
1525
+ # {
1526
+ # "member": {
1527
+ # "id": "person-id", "firstName": "Jane", "lastName": "Doe",
1528
+ # "phoneNumber": "+12223334444", "extension": "1234",
1529
+ # "locationId": "location-id"
1530
+ # }
1531
+ # },
1532
+ # {
1533
+ # "callparkextension": {
1534
+ # "id": "cpe-id", "name": "CPE 1001", "extension": "1001"
1535
+ # }
1536
+ # }
1537
+ # ]
1538
+ # }
1539
+ ```
1540
+
1541
+ #### Configure Monitoring Settings
1542
+
1543
+ ```
1544
+ PUT https://webexapis.com/v1/people/{personId}/features/monitoring
1545
+ ```
1546
+
1547
+ ```python
1548
+ body = {
1549
+ "enableCallParkNotification": True,
1550
+ "monitoredElements": ["person-id-1", "person-id-2", "cpe-id-1"]
1551
+ }
1552
+ api.session.rest_put(f"{BASE}/people/{person_id}/features/monitoring", json=body)
1553
+ ```
1554
+
1555
+ **Gotchas:**
1556
+ - Maximum 50 monitored elements.
1557
+ - For updates, pass a flat list of IDs (person, place, or call park extension IDs). Reads return full objects with nested `member` or `callparkextension` details.
1558
+ - The JSON key for call park extensions is `callparkextension` (lowercase, no hyphen).
1559
+
1560
+ ---
1561
+
1562
+ ## 10. Push-to-Talk
1563
+
1564
+ **API class:** `PushToTalkApi`
1565
+ **Feature key:** `pushToTalk`
1566
+ **Source:** `wxc_sdk/person_settings/push_to_talk.py`
1567
+
1568
+ Allows desk phones to function as one-way or two-way intercoms connecting people in different parts of the organization.
1569
+
1570
+ ### Data Models
1571
+
1572
+ #### `PTTConnectionType` (Enum)
1573
+
1574
+ | Value | Description |
1575
+ |-------|-------------|
1576
+ | `ONE_WAY` | Initiators can chat but the target cannot respond |
1577
+ | `TWO_WAY` | Both parties can communicate |
1578
+
1579
+ #### `PushToTalkAccessType` (Enum)
1580
+
1581
+ | Value | Description |
1582
+ |-------|-------------|
1583
+ | `ALLOW_MEMBERS` | List of people allowed to use PTT with this person |
1584
+ | `BLOCK_MEMBERS` | List of people blocked from using PTT with this person |
1585
+
1586
+ #### `PushToTalkSettings`
1587
+
1588
+ | Field | Type | Description |
1589
+ |-------|------|-------------|
1590
+ | `allow_auto_answer` | `bool` | When enabled, person receives and auto-answers PTT calls |
1591
+ | `connection_type` | `PTTConnectionType` | One-way or two-way |
1592
+ | `access_type` | `PushToTalkAccessType` | Allow-list or block-list mode |
1593
+ | `members` | `list[Union[str, MonitoredMember]]` | People allowed or blocked. For updates, can pass member IDs (strings) directly. |
1594
+
1595
+ ### Methods
1596
+
1597
+ #### `read`
1598
+
1599
+ ```python
1600
+ PushToTalkApi.read(entity_id: str, org_id: str = None) -> PushToTalkSettings
1601
+ ```
1602
+
1603
+ - **Scopes:** `spark-admin:people_read`
1604
+
1605
+ #### `configure`
1606
+
1607
+ ```python
1608
+ PushToTalkApi.configure(entity_id: str, settings: PushToTalkSettings, org_id: str = None)
1609
+ ```
1610
+
1611
+ - **Scopes:** `spark-admin:people_write`
1612
+ - Members are flattened to a list of IDs before sending (extracted from `MonitoredMember.member_id` or passed as strings).
1613
+ - Serializes with `exclude_none=False` and `exclude_unset=True`, so explicitly set `None` values are preserved in the request.
1614
+
1615
+ ### CLI Examples
1616
+
1617
+ ```bash
1618
+ # Read push-to-talk settings for a person
1619
+ wxcli user-settings list-push-to-talk PERSON_ID
1620
+
1621
+ # Read push-to-talk settings as JSON
1622
+ wxcli user-settings list-push-to-talk PERSON_ID -o json
1623
+
1624
+ # Enable push-to-talk with auto-answer
1625
+ wxcli user-settings update-push-to-talk PERSON_ID --allow-auto-answer
1626
+
1627
+ # Disable push-to-talk auto-answer
1628
+ wxcli user-settings update-push-to-talk PERSON_ID --no-allow-auto-answer
1629
+
1630
+ # Full push-to-talk config with connection type and members (requires --json-body)
1631
+ wxcli user-settings update-push-to-talk PERSON_ID --json-body '{
1632
+ "allowAutoAnswer": true,
1633
+ "connectionType": "TWO_WAY",
1634
+ "accessType": "ALLOW_MEMBERS",
1635
+ "members": ["PERSON_ID_1", "PERSON_ID_2"]
1636
+ }'
1637
+ ```
1638
+
1639
+ ### Raw HTTP
1640
+
1641
+ #### Read Push-to-Talk Settings
1642
+
1643
+ ```
1644
+ GET https://webexapis.com/v1/people/{personId}/features/pushToTalk
1645
+ ```
1646
+
1647
+ ```python
1648
+ result = api.session.rest_get(f"{BASE}/people/{person_id}/features/pushToTalk")
1649
+ # Response:
1650
+ # {
1651
+ # "allowAutoAnswer": true,
1652
+ # "connectionType": "TWO_WAY",
1653
+ # "accessType": "ALLOW_MEMBERS",
1654
+ # "members": [
1655
+ # {"id": "person-id", "firstName": "Jane", "lastName": "Doe", ...}
1656
+ # ]
1657
+ # }
1658
+ ```
1659
+
1660
+ #### Configure Push-to-Talk Settings
1661
+
1662
+ ```
1663
+ PUT https://webexapis.com/v1/people/{personId}/features/pushToTalk
1664
+ ```
1665
+
1666
+ ```python
1667
+ body = {
1668
+ "allowAutoAnswer": True,
1669
+ "connectionType": "TWO_WAY",
1670
+ "accessType": "ALLOW_MEMBERS",
1671
+ "members": ["person-id-1", "person-id-2"]
1672
+ }
1673
+ api.session.rest_put(f"{BASE}/people/{person_id}/features/pushToTalk", json=body)
1674
+ ```
1675
+
1676
+ **Gotchas:**
1677
+ - `members` accepts a flat list of person IDs for updates, even though reads return full member objects.
1678
+ - `connectionType` values: `"ONE_WAY"`, `"TWO_WAY"`.
1679
+ - `accessType` values: `"ALLOW_MEMBERS"`, `"BLOCK_MEMBERS"`.
1680
+
1681
+ ---
1682
+
1683
+ ## 11. Music on Hold
1684
+
1685
+ **API class:** `MusicOnHoldApi`
1686
+ **Feature key:** `musicOnHold`
1687
+ **Source:** `wxc_sdk/person_settings/moh.py`
1688
+
1689
+ Music played when a caller is put on hold or the call is parked.
1690
+
1691
+ ### Data Model
1692
+
1693
+ #### `MusicOnHold`
1694
+
1695
+ | Field | Type | Description |
1696
+ |-------|------|-------------|
1697
+ | `moh_enabled` | `bool` | Music on hold enabled/disabled for the entity |
1698
+ | `moh_location_enabled` | `bool` | Read-only. Whether MoH is enabled at the location level. Excluded from updates. |
1699
+ | `greeting` | `Greeting` | `DEFAULT` or `CUSTOM` |
1700
+ | `audio_announcement_file` | `AnnAudioFile` | Custom audio file details (when greeting is `CUSTOM`) |
1701
+
1702
+ **Important interaction between `moh_location_enabled` and `moh_enabled`:**
1703
+
1704
+ | `moh_location_enabled` | `moh_enabled` | Result |
1705
+ |------------------------|---------------|--------|
1706
+ | `false` | `true` | MoH is **disabled** for user |
1707
+ | `true` | `false` | MoH is **off** for user |
1708
+ | `true` | `true` | MoH plays for user |
1709
+
1710
+ ### Methods
1711
+
1712
+ #### `read`
1713
+
1714
+ ```python
1715
+ MusicOnHoldApi.read(entity_id: str, org_id: str = None) -> MusicOnHold
1716
+ ```
1717
+
1718
+ - **Scopes:** `spark-admin:telephony_config_read`
1719
+
1720
+ #### `configure`
1721
+
1722
+ ```python
1723
+ MusicOnHoldApi.configure(entity_id: str, settings: MusicOnHold, org_id: str = None)
1724
+ ```
1725
+
1726
+ - **Scopes:** `spark-admin:telephony_config_write`
1727
+ - **Prerequisite:** Music on hold must be enabled at the location level for the person-level setting to take effect.
1728
+
1729
+ ### CLI Examples
1730
+
1731
+ ```bash
1732
+ # Read music on hold settings for a person
1733
+ wxcli user-settings show-music-on-hold PERSON_ID
1734
+
1735
+ # Read music on hold settings as JSON
1736
+ wxcli user-settings show-music-on-hold PERSON_ID -o json
1737
+
1738
+ # Enable music on hold with default greeting
1739
+ wxcli user-settings update-music-on-hold PERSON_ID --moh-enabled --greeting DEFAULT
1740
+
1741
+ # Enable music on hold with custom greeting
1742
+ wxcli user-settings update-music-on-hold PERSON_ID --moh-enabled --greeting CUSTOM
1743
+
1744
+ # Disable music on hold
1745
+ wxcli user-settings update-music-on-hold PERSON_ID --no-moh-enabled
1746
+
1747
+ # Full music on hold config with audio file reference (requires --json-body)
1748
+ wxcli user-settings update-music-on-hold PERSON_ID --json-body '{
1749
+ "mohEnabled": true,
1750
+ "greeting": "CUSTOM",
1751
+ "audioAnnouncementFile": {"id": "ANNOUNCEMENT_FILE_ID"}
1752
+ }'
1753
+ ```
1754
+
1755
+ ### Raw HTTP
1756
+
1757
+ > **Note:** Music on Hold uses the `telephony/config/people` path, not `people/{id}/features`.
1758
+
1759
+ #### Read Music on Hold Settings
1760
+
1761
+ ```
1762
+ GET https://webexapis.com/v1/telephony/config/people/{personId}/musicOnHold
1763
+ ```
1764
+
1765
+ ```python
1766
+ result = api.session.rest_get(f"{BASE}/telephony/config/people/{person_id}/musicOnHold")
1767
+ # Response:
1768
+ # {
1769
+ # "mohEnabled": true,
1770
+ # "mohLocationEnabled": true,
1771
+ # "greeting": "DEFAULT",
1772
+ # "audioAnnouncementFile": {...}
1773
+ # }
1774
+ ```
1775
+
1776
+ #### Configure Music on Hold Settings
1777
+
1778
+ ```
1779
+ PUT https://webexapis.com/v1/telephony/config/people/{personId}/musicOnHold
1780
+ ```
1781
+
1782
+ ```python
1783
+ body = {
1784
+ "mohEnabled": True,
1785
+ "greeting": "DEFAULT",
1786
+ "audioAnnouncementFile": {"id": "announcement-file-id"}
1787
+ }
1788
+ api.session.rest_put(f"{BASE}/telephony/config/people/{person_id}/musicOnHold", json=body)
1789
+ ```
1790
+
1791
+ **Gotchas:**
1792
+ - `mohLocationEnabled` is read-only; do not include in PUT body.
1793
+ - Person-level MoH only works if MoH is also enabled at the location level (`mohLocationEnabled` must be `true`).
1794
+ - Uses `spark-admin:telephony_config_read`/`write` scopes (not `people_read`/`write`).
1795
+
1796
+ ---
1797
+
1798
+ ## 12. Common Patterns
1799
+
1800
+ ### URL Construction
1801
+
1802
+ All APIs in this group use `PersonSettingsApiChild.f_ep()` to build endpoints. The pattern depends on the entity type:
1803
+
1804
+ | Entity Type | URL Pattern |
1805
+ |-------------|-------------|
1806
+ | Person | `people/{entity_id}/features/{feature}` |
1807
+ | Workspace | `workspaces/{entity_id}/features/{feature}` |
1808
+ | Virtual Line | `telephony/config/virtualLines/{entity_id}/{feature}` |
1809
+ | Location | `telephony/config/locations/{entity_id}/{feature}` |
1810
+
1811
+ Some features have alternate URL mappings (defined in `common.py`). Notable remaps for persons:
1812
+
1813
+ - `agent` -> `telephony/config/people/{id}/agent/...`
1814
+ - `musicOnHold` -> `telephony/config/people/{id}/musicOnHold`
1815
+ - `voicemail` passcode -> `telephony/config/people/{id}/voicemail/passcode`
1816
+
1817
+ ### Read/Update Pattern
1818
+
1819
+ Every sub-API follows the same pattern:
1820
+
1821
+ 1. `read(entity_id, org_id=None)` -- GET request, returns a data model
1822
+ 2. `configure(entity_id, settings, org_id=None)` -- PUT request with JSON body
1823
+ 3. Some APIs have additional action methods (greeting uploads, PIN resets)
1824
+
1825
+ ### Scopes Summary
1826
+
1827
+ | Operation | Typical Admin Scope | Typical Self Scope |
1828
+ |-----------|--------------------|--------------------|
1829
+ | Read settings | `spark-admin:people_read` | `spark:people_read` |
1830
+ | Update settings | `spark-admin:people_write` | `spark:people_write` |
1831
+ | Telephony config read | `spark-admin:telephony_config_read` | -- |
1832
+ | Telephony config write | `spark-admin:telephony_config_write` | -- |
1833
+
1834
+ APIs using `telephony_config_*` scopes: Agent Caller ID, Music on Hold, Voicemail passcode.
1835
+
1836
+ ### Greeting/Audio Upload Pattern
1837
+
1838
+ Three APIs support custom audio upload (all `.wav` format, multipart/form-data):
1839
+
1840
+ | API | Method | Endpoint Suffix |
1841
+ |-----|--------|----------------|
1842
+ | Voicemail | `configure_busy_greeting()` | `actions/uploadBusyGreeting/invoke` |
1843
+ | Voicemail | `configure_no_answer_greeting()` | `actions/uploadNoAnswerGreeting/invoke` |
1844
+ | Call Intercept | `greeting()` | `actions/announcementUpload/invoke` |
1845
+
1846
+ All accept either a file path (str) or `BufferedReader`. When passing a reader, `upload_as` (a `.wav` filename) is required.
1847
+
1848
+ ### Update Exclusions (Read-Only Fields)
1849
+
1850
+ Each model's `update()` method strips fields that cannot be written:
1851
+
1852
+ | Model | Excluded Fields |
1853
+ |-------|----------------|
1854
+ | `VoicemailSettings` | `greeting_uploaded`, `system_max_number_of_rings`, `voice_message_forwarding_enabled` |
1855
+ | `CallerId` | `caller_id_types` (not in `fields_for_update`) |
1856
+ | `CallRecordingSetting` | `service_provider`, `external_group`, `external_identifier` |
1857
+ | `InterceptSetting` | `incoming.announcements.file_name` |
1858
+ | `MusicOnHold` | `moh_location_enabled` |
1859
+
1860
+ ### Entity Applicability
1861
+
1862
+ Not all settings apply equally to all entity types:
1863
+
1864
+ | Setting | Person | Workspace | Virtual Line |
1865
+ |---------|--------|-----------|-------------|
1866
+ | Voicemail | Yes | Yes | Yes |
1867
+ | Caller ID | Yes | Yes | Yes |
1868
+ | Agent Caller ID | Yes | -- | Yes |
1869
+ | Anonymous Call Rejection | No (admin `/people/{id}` endpoint does not exist; self via `/me` only) | Yes (Professional-licensed workspaces only) | -- |
1870
+ | Privacy | Yes | Yes | Yes |
1871
+ | Barge-In | Yes | Yes | Yes |
1872
+ | Call Recording | Yes | Yes | Yes |
1873
+ | Call Intercept | Yes | Yes | Yes |
1874
+ | Monitoring | Yes | Yes | -- |
1875
+ | Push-to-Talk | Yes | Yes | Yes |
1876
+ | Music on Hold | Yes | Yes | Yes |
1877
+
1878
+ ---
1879
+
1880
+ ## Gotchas (Cross-Cutting)
1881
+
1882
+ - **Read-only fields cause silent failures.** Every PUT endpoint in this group has read-only fields that must be excluded from the request body. If you include them, the API may return 400 or silently ignore the entire request. Key offenders: `greetingUploaded`, `systemMaxNumberOfRings`, `voiceMessageForwardingEnabled` (voicemail); `types`, `locationExternalCallerIdName` (caller ID); `serviceProvider`, `externalGroup`, `externalIdentifier` (call recording); `fileName` (call intercept); `mohLocationEnabled` (music on hold).
1883
+ - **Nested settings require `--json-body`.** The wxcli generator skips deeply nested object/array body fields. For voicemail (sendBusyCalls, sendUnansweredCalls, notifications, etc.), call intercept (incoming/outgoing), call recording (notification, repeat, startStopAnnouncement), monitoring (monitoredElements), push-to-talk (members), and privacy (monitoringAgents), use `--json-body` with the full JSON payload.
1884
+ - **Three different URL path patterns.** Most features use `people/{id}/features/{feature}`, but Agent Caller ID uses `telephony/config/people/{id}/agent/...`, Music on Hold uses `telephony/config/people/{id}/musicOnHold`, and Voicemail Passcode uses `telephony/config/people/{id}/voicemail/passcode`. The corresponding scopes differ too (`telephony_config_*` vs `people_*`).
1885
+ - **Scope mismatch between read and write.** Agent Caller ID uses `people_read` for listing available IDs but `telephony_config_read` for reading the current selection. Always check the specific scope required per method.
1886
+ - **Music on Hold depends on location-level setting.** Even if `mohEnabled` is `true` for a person, MoH will not play unless `mohLocationEnabled` is also `true` at the location level. The person-level API cannot change the location-level setting.
1887
+ - **`notification.type` null serialization.** Call Recording's `notification.type` returns the string `"None"` on reads but expects JSON `null` on writes. In wxcli, pass `"type": null` in `--json-body`.
1888
+ - **Anonymous Call Rejection has NO admin-level person endpoint.** Live API testing confirms that `GET /v1/people/{personId}/features/anonymousCallReject` and `GET /v1/telephony/config/people/{personId}/anonymousCallReject` both return 404. The only working paths are: (1) workspace admin: `/telephony/config/workspaces/{workspaceId}/anonymousCallReject` (requires Professional-licensed workspace, error 25409 otherwise), and (2) self-service: `/telephony/config/people/me/settings/anonymousCallReject` (requires calling-licensed user token with `spark:telephony_config_read/write`). The wxcli CLI only exposes it under `workspace-settings`, not `user-settings`.
1889
+ - **Monitoring has a 50-element maximum.** The `monitoredElements` list accepts at most 50 entries (persons, places, virtual lines, and call park extensions combined). The API does not surface this limit in error messages clearly.
1890
+ - **Members lists return objects but accept IDs.** Privacy (`monitoringAgents`), Monitoring (`monitoredElements`), and Push-to-Talk (`members`) all return full member objects on reads but accept flat lists of ID strings on writes.
1891
+
1892
+ ---
1893
+
1894
+ ## See Also
1895
+
1896
+ - **[Location Call Settings — Core](location-calling-core.md)** — Location-level voicemail policies (transcription toggle), org-wide voicemail settings (expiry, forwarding), and location-level call intercept that govern person-level defaults
1897
+ - **[Location Recording — Advanced](location-recording-advanced.md)** — Location/org-level call recording vendor settings that must be configured before per-person recording works
1898
+ - **[self-service-call-settings.md](self-service-call-settings.md)** -- User-level `/people/me/` endpoints for self-service call settings, including 6 user-only settings with no admin path.