concepts/architecture-and-lifecycle.md
# Zoom Phone Architecture and Lifecycle
## Architecture
```text
User/Agent UI
|
| (A) Smart Embed postMessage events
v
Smart Embed Iframe (applications.zoom.us)
|
| event stream + call controls
v
CRM Web App (event bridge + UI state)
|
| OAuth token on server only
v
Backend API Layer
|\
| \-- Zoom Phone REST APIs (call history, call handling, contacts)
|
\---- Webhook endpoint (phone.* events)
```
## Lifecycle Workflow
1. Provision:
- Account has Zoom Phone and optional SMS enablement.
2. Authorize:
- OAuth app installed and scoped for required Phone operations.
3. Initialize UI:
- Load Smart Embed iframe/script.
- Wait for `onZoomPhoneIframeApiReady`.
- Send `zp-init-config` and register event handlers.
4. Engage:
- Start calls/SMS via `zp-make-call` or `zp-input-sms`.
- Receive events (`zp-call-*`, `zp-sms-log-event`, optional AI/contact/notes events).
5. Persist:
- Save event snapshots keyed by `callId`.
- Reconcile to call history/call element records after completion.
6. Post-call:
- Save call notes/disposition and optional recording/voicemail links.
7. Operate:
- Track deprecations and apply endpoint/event mapping updates.
## Version Drift Strategy
- Normalize inbound payloads in one adapter layer.
- Keep endpoint constants centralized by version target.
- Feature-flag optional payload fields.
- Keep webhook + Smart Embed event handlers tolerant to added fields and enum expansion.
examples/phone-api-service-pattern.md
# Phone API Service Pattern (Migration-Safe)
## Pattern goals
- Isolate OAuth token usage to server code.
- Support current call history/call element model.
- Keep compatibility with old payload fields while migrating.
## Service example
```javascript
export async function getCallHistory(accessToken, from, to) {
const qs = new URLSearchParams({ from, to }).toString();
const res = await fetch(`https://api.zoom.us/v2/phone/call_history?${qs}`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!res.ok) throw new Error(`call_history failed: ${res.status}`);
const data = await res.json();
// Normalize v2/v3 style for downstream code.
return (data.call_history || data.call_logs || []).map((row) => ({
callHistoryUuid: row.call_history_uuid || row.id,
callId: row.call_id,
raw: row,
}));
}
export async function getCallElement(accessToken, callElementId) {
const res = await fetch(`https://api.zoom.us/v2/phone/call_element/${callElementId}`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
if (!res.ok) throw new Error(`call_element failed: ${res.status}`);
return res.json();
}
```
## Operational notes
- Add explicit logging when fallback fields (`call_logs`, `call_path`) are encountered.
- Remove fallback path once migration is complete.
examples/smart-embed-postmessage-bridge.md
# Smart Embed postMessage Bridge Pattern
## Why this pattern
Smart Embed event/control flow is `window.postMessage` based. Reliability depends on strict initialization order and origin validation.
## Pattern
```javascript
const ZOOM_ORIGIN = 'https://applications.zoom.us';
const iframe = document.querySelector('#zoom-embeddable-phone-iframe');
function initSmartEmbed(config) {
iframe?.contentWindow?.postMessage({
type: 'zp-init-config',
data: config,
}, ZOOM_ORIGIN);
}
function makeCall(number, callerId) {
iframe?.contentWindow?.postMessage({
type: 'zp-make-call',
data: { number, callerId, autoDial: true },
}, ZOOM_ORIGIN);
}
window.addEventListener('message', (event) => {
if (event.origin !== ZOOM_ORIGIN) return;
const payload = event.data;
if (!payload?.type) return;
switch (payload.type) {
case 'zp-call-ringing-event':
case 'zp-call-connected-event':
case 'zp-call-ended-event':
case 'zp-call-log-completed-event':
handlePhoneEvent(payload);
break;
default:
break;
}
});
```
## Operational notes
- Call APIs only after iframe readiness callback.
- Persist `event.id` (if present) for idempotency.
- Keep event dispatcher tolerant to new event types.
references/call-handling-patterns.md
# Call Handling API Patterns
## Endpoint family
- `POST /phone/extension/{extensionId}/call_handling/settings/{settingType}`
- `PATCH /phone/extension/{extensionId}/call_handling/settings/{settingType}`
- `GET /phone/extension/{extensionId}/call_handling/settings`
## Supported extension targets
- Users
- Auto receptionists
- Call queues
## Common subsettings
- `custom_hours`
- `holiday`
- `call_handling`
- `call_forwarding` (user-focused)
## Practical implementation pattern
1. Read current settings snapshot with `GET`.
2. Build small, typed patch payloads by subsetting.
3. Update business/closed/holiday hours independently.
4. Validate E.164 formatting for external phone numbers.
5. Store previous settings for rollback.
## Drift watchpoints
- Enum/action values may evolve.
- Routing field names differ between docs sections and old implementations.
- Keep a server-side validator to reject malformed call-handling payloads before API call.
references/crm-sample-validation.md
# CRM Sample Validation (https://github.com/zoom/CRM-Sample)
## Useful architecture patterns adopted
- Smart Embed as dedicated iframe sidebar component.
- Server-only OAuth token handling with `next-auth` callbacks.
- API route pattern that reads session token and calls Phone APIs.
- Client-side event listener for Smart Embed message events.
## Environment keys observed in sample
- `ZOOM_CLIENT_ID`
- `ZOOM_CLIENT_SECRET`
- `NEXTAUTH_URL`
- `NEXTAUTH_SECRET`
## Lifecycle pattern extracted
1. User authenticates with Zoom OAuth.
2. Server stores access/refresh token session state.
3. UI renders Smart Embed iframe.
4. UI sends click-to-call command and listens for events.
5. Backend fetches call history/contact data for CRM views.
## Contradictions and drift issues found
- Sample still maps response via `data.call_logs` (legacy shape) while migration docs push toward call history/call element shapes.
- README references `.env.example`, repository provides `.env.sample`.
- Middleware matcher and route naming are inconsistent (`/call-log` vs `/call-logs`, missing `/api/calls/[id]` route used by modal).
- Sample contains hardcoded demo records in some screens alongside live API calls.
## Guidance
- Treat sample as architectural reference, not canonical API contract.
- Apply migration-safe normalizers for call history fields.
- Validate each endpoint payload against current Phone API docs.
references/deprecations-and-migrations.md
# Deprecations and Migration Notes (Zoom Phone)
## Timeline extracted from docs
- Legacy Call Logs API (v1) full deprecation: **April 2026**.
- Legacy Call Log webhooks (v1) full deprecation: **May 2026**.
- Legacy array fields deprecation:
- `call_log` array deprecation: **November 2026**.
- `call_path` array deprecation: **November 2026**.
## API migration map
- `GET /phone/call_logs` -> `GET /phone/call_history`
- `GET /phone/call_logs/{callLogId}` -> `GET /phone/call_history/{call_history_uuid}`
- `GET /phone/call_history_detail/{callHistoryId}` -> `GET /phone/call_element/{call_element_id}`
## Webhook migration map
- `phone.call_log_deleted` -> `phone.call_history_deleted` -> `phone.call_element_deleted`
- `phone.callee_call_log_completed` -> `phone.callee_call_history_completed` -> `phone.callee_call_element_completed`
- `phone.caller_call_log_completed` -> `phone.caller_call_history_completed` -> `phone.caller_call_element_completed`
## Compatibility strategy
- Standardize storage fields:
- `call_id`
- `call_history_uuid`
- `call_element_id`
- Add adapters for old/new field names during transition windows.
- Prefer v3 naming for all new features and schemas.
references/environment-variables.md
# Zoom Phone Environment Variables
## Standard `.env` keys
| Variable | Required | Used for | Where to find |
| --- | --- | --- | --- |
| `ZOOM_CLIENT_ID` | Yes | OAuth app identity for Phone APIs | Zoom Marketplace -> General OAuth app -> App Credentials |
| `ZOOM_CLIENT_SECRET` | Yes | OAuth token exchange | Zoom Marketplace -> General OAuth app -> App Credentials |
| `ZOOM_REDIRECT_URI` | Yes (user OAuth) | OAuth callback URL | Zoom Marketplace -> OAuth redirect/allow list |
| `ZOOM_ACCOUNT_ID` | Optional (S2S patterns) | Account-level service integrations | Zoom Marketplace -> Server-to-Server OAuth app credentials |
| `ZOOM_WEBHOOK_SECRET` or `WEBHOOK_SECRET_TOKEN` | Recommended | Webhook signature verification | Zoom Marketplace -> Features -> Event Subscriptions -> Secret Token |
| `ZOOM_PHONE_SMART_EMBED_URL` | Optional | Smart Embed iframe URL override | Zoom Phone Smart Embed docs (`applications.zoom.us` path) |
| `ZOOM_PHONE_SMART_EMBED_ORIGIN` | Recommended | Allowed postMessage origin | Set to `https://applications.zoom.us` |
## Common runtime keys
- `NEXTAUTH_URL` (if using NextAuth)
- `NEXTAUTH_SECRET` (if using NextAuth)
- `PORT`
- `NODE_ENV`
## Notes
- Keep OAuth secrets server-side only.
- Smart Embed approved domains are configured in Marketplace app settings, not in `.env`.
- Re-authorize app after changing scopes.
references/forum-top-questions.md
---
title: "Forum-Derived Top Questions (Phone)"
---
# Forum-Derived Top Questions (Phone)
Use this as a checklist of the most common recent Developer Forum asks for Zoom Phone integrations.
## Fast Routing Questions (Ask First)
- Integration surface: Smart Embed, Phone REST API, webhooks, or URI launch (`zoomphonecall://`, `zoomphonesms://`).
- App/auth type: Server-to-Server OAuth vs user OAuth, and who the token is acting as.
- Account posture: Zoom Phone license assigned, user enabled, admin permissions, site/queue scope.
- Exact failure: HTTP status + Zoom `code`/`message` + endpoint/event name + sample payload.
- Correlation IDs available: `call_id`, `call_history_uuid`, `call_element_id`, recording ID.
## Smart Embed Sign-In or Calling Fails
Common asks:
- Smart Embed shows login but never completes.
- Widget loads, but outbound/inbound calling does not work.
- `zp-make-call`/search-and-match behaviors are inconsistent.
Answer pattern:
- Confirm approved Smart Embed domain matches the real runtime origin exactly.
- Confirm `origin` parameter is domain-level where required and not path-mismatched.
- Verify Zoom client sign-in state and account licensing prerequisites.
- Add strict `postMessage` origin handling and validate event init sequence.
## `call_logs` to `call_history` Migration Gaps
Common asks:
- Missing fields after migrating to `call_history`.
- Existing call analytics pipelines break after deprecation migration.
Answer pattern:
- Treat migration as a schema migration, not a drop-in endpoint swap.
- Build a mapping layer from legacy fields to current call history/call element fields.
- Persist both legacy and new IDs during transition for reconciliation.
- Update downstream reports that assumed removed fields.
## Recording and Download URL Auth Errors
Common asks:
- `download_url` returns 401/403.
- `Invalid access token, does not contain scopes` on recordings/transcripts.
Answer pattern:
- Generate a fresh token from the app that owns the needed scopes.
- Re-authorize after scope changes; verify token scope set, not just app config.
- Handle redirects while preserving auth headers where needed.
- Keep a fallback retry path for temporary scope/permission regressions.
## "Zoom Phone Has Not Been Enabled" (`2013`/`2031`)
Common asks:
- Token works for some APIs/users but Phone endpoints return not enabled.
Answer pattern:
- Verify `account_id` is present in S2S token request and token is from expected account.
- Verify target users actually have Zoom Phone entitlement.
- Verify caller/admin context has permission for account-level Phone resources.
- Re-test with one known-good licensed admin and one known-good licensed user.
## Webhooks: Missing Events or Duplicates
Common asks:
- Expected call events not received.
- Missed-call events delivered more than once.
Answer pattern:
- Acknowledge webhooks quickly with `200`/`204` and process asynchronously.
- Implement idempotency keyed by event ID/call identifiers.
- Expect retries and occasional ordering variance.
- Validate event subscription scope and verify webhook logs before blaming delivery.
## Correlating Calls Across APIs and Events
Common asks:
- Hard to tie recordings, call path/history, and webhook events to one interaction.
Answer pattern:
- Persist all call identifiers emitted at each lifecycle phase.
- Build a correlation table keyed by your internal interaction ID.
- Do not rely on a single identifier across all endpoints.
## Pagination and Incomplete Result Sets
Common asks:
- `/phone/users` or call list endpoints appear to miss records.
Answer pattern:
- Always iterate `next_page_token` until exhausted.
- Keep query filters stable between page requests.
- Add dedupe + page-audit logging to detect loops or repeated pages.
references/smart-embed-event-contract.md
# Smart Embed Event Contract
## Core initialization and command messages
- `zp-init-config`
- `zp-make-call`
- `zp-input-sms`
- `zp-contact-search-response`
- `zp-contact-match-response`
## Core emitted event types
- `zp-call-ringing-event`
- `zp-call-connected-event`
- `zp-call-ended-event`
- `zp-call-log-completed-event`
- `zp-call-recording-completed-event`
- `zp-call-voicemail-received-event`
- `zp-ai-call-summary-event`
- `zp-sms-log-event`
- `zp-save-log-event`
- `zp-contact-search-event`
- `zp-contact-match-event`
- `zp-notes-save-event`
## Field-level reliability notes
- `callId` appears early in lifecycle.
- `callLogId` appears in completion-oriented events.
- `event.id` can be used for deduplication/idempotency.
- Additional flags can appear (for example `enableAutoLog` behavior fields).
## Security and resilience
- Validate `event.origin === https://applications.zoom.us`.
- Keep a permissive parser for new optional fields.
- Route unknown event types into structured logs, not hard failures.
references/source-map.md
# Zoom Phone Source Map
Crawled docs source:
- `https://developers.zoom.us/docs/phone/`
- Crawl config used: depth `10`, concurrency `10`, Android excluded.
## Processed pages
- `call-data.md`
- `call-handling.md`
- `create-app.md`
- `first-app.md`
- `integrate-with-zoom-phone.md`
- `migrate.md`
- `outbound-call.md`
- `outbound-sms.md`
- `smart-embed-guide.md`
- `smart-embed.md`
- `start.md`
- `webhook-migrate.md`
## Mapping to skill docs
- App setup + OAuth -> [../SKILL.md](../SKILL.md), [environment-variables.md](environment-variables.md)
- Smart Embed lifecycle/events -> `examples/smart-embed-postmessage-bridge.md`, `references/smart-embed-event-contract.md`
- Call handling admin API -> `references/call-handling-patterns.md`
- API/webhook migration timeline -> `references/deprecations-and-migrations.md`
- CRM sample validation -> `references/crm-sample-validation.md`
RUNBOOK.md
# Zoom Phone 5-Minute Preflight Runbook
Use this before deep debugging.
## 1) Confirm Product Prerequisites
- Zoom Phone licenses assigned.
- Admin access available for Phone settings.
- If SMS is required, 10DLC/SMS setup is complete.
## 2) Confirm App and OAuth
- App type: General OAuth app for user/admin flows.
- Redirect URI and allow list are exact and current.
- Required Phone scopes are added.
- App is installed/re-authorized after scope changes.
## 3) Confirm Integration Surface
- Smart Embed: iframe/script loaded and approved domain configured.
- API/Webhook: access token valid and webhook endpoint reachable.
- URI launch: endpoint uses supported scheme and client is signed in.
## 4) Confirm Event/Data Correlation
- Persist `call_id` for real-time events.
- Persist `call_history_uuid` and `call_element_id` for post-call lookup.
- Keep idempotency logic for duplicate event deliveries.
## 5) Confirm Migration Posture
- Do not build new features on legacy v1 call logs.
- Webhook consumers are ready for `call_element` event names/fields.
- Field-mapping adapter exists for old/new payload shapes.
## 6) Confirm Security Controls
- Smart Embed `postMessage` enforces trusted origin.
- Webhook signatures validated with secret token.
- OAuth secrets are server-side only.
## 7) Fast Decision Tree
- Smart Embed iframe visible but no events -> missing init sequence or bad origin filtering.
- OAuth works but API fails with 401/403 -> scope mismatch or stale authorization.
- Data pipeline breaks after endpoint/event upgrade -> missing v2/v3 field mapping.
- URI click does nothing -> unsupported platform/client state, or wrong scheme.
scenarios/high-level-scenarios.md
# Zoom Phone High-Level Scenarios
## 1) Smart Embed CRM Softphone
Use Smart Embed in a CRM sidebar to place and receive calls, then log outcomes back into CRM records.
## 2) Click-to-Call from Lead Table
Use `zp-make-call` or URI scheme launch from contact rows; subscribe to call status events for UI updates.
## 3) SMS Follow-Up Automation
Use `zoomphonesms://` and Smart Embed SMS events to trigger follow-up tasks and SLA timers.
## 4) Call Disposition + Notes Pipeline
Use `zp-save-log-event` and `zp-notes-save-event` to capture custom dispositions and sync to third-party systems.
## 5) Real-Time Supervisor Dashboard
Use `phone.*` webhooks and call events to track active calls, misses, rejects, and queue pressure.
## 6) Call History Modernization
Migrate from legacy call log fields to call history/call element IDs while maintaining backward compatibility for old records.
## 7) Call Handling Admin Automation
Use call handling APIs to standardize business/closed/holiday routing for users, auto receptionists, and call queues.
## 8) Blended Phone + Contact Center Journey
Route phone interactions into Contact Center follow-up or escalation workflows using shared CRM context.
SKILL.md
---
name: build-zoom-phone-integration
description: "Reference skill for Zoom Phone. Use after routing to a phone workflow when implementing OAuth, Phone APIs, webhooks, Smart Embed events, URI schemes, CRM or CTI dialers, or call handling automation."
triggers:
- "zoom phone"
- "phone smart embed"
- "zoom phone api"
- "zoom phone webhook"
- "call history"
- "call handling"
- "zoomphonecall"
- "zoomphonesms"
- "phone crm integration"
- "call element"
---
# /build-zoom-phone-integration
Background reference for Zoom Phone integrations across API, webhook, Smart Embed, and URI-launch workflows.
Implementation guidance for Zoom Phone integrations across API, webhook/event, Smart Embed, and URI-launch workflows.
Official docs:
- https://developers.zoom.us/docs/phone/
- CRM sample reference: https://github.com/zoom/CRM-Sample
## Routing Guardrail
- If the user needs embedded softphone behavior in a web app, use Smart Embed ([examples/smart-embed-postmessage-bridge.md](examples/smart-embed-postmessage-bridge.md)).
- If the user needs call records, analytics, or automation, use Phone REST API and webhooks ([references/deprecations-and-migrations.md](references/deprecations-and-migrations.md)).
- If the user needs click-to-dial/SMS launch from external UI, use URI schemes (`zoomphonecall://`, `zoomphonesms://`).
- If the user mixes Zoom Phone and Contact Center, chain with [../contact-center/SKILL.md](../contact-center/SKILL.md).
## Quick Links
Start here:
1. [concepts/architecture-and-lifecycle.md](concepts/architecture-and-lifecycle.md)
2. [scenarios/high-level-scenarios.md](scenarios/high-level-scenarios.md)
3. [references/deprecations-and-migrations.md](references/deprecations-and-migrations.md)
4. [references/forum-top-questions.md](references/forum-top-questions.md)
5. [references/smart-embed-event-contract.md](references/smart-embed-event-contract.md)
6. [references/call-handling-patterns.md](references/call-handling-patterns.md)
7. [references/environment-variables.md](references/environment-variables.md)
8. [references/crm-sample-validation.md](references/crm-sample-validation.md)
9. [troubleshooting/common-issues.md](troubleshooting/common-issues.md)
10. [RUNBOOK.md](RUNBOOK.md)
11. [examples/smart-embed-postmessage-bridge.md](examples/smart-embed-postmessage-bridge.md)
12. [examples/phone-api-service-pattern.md](examples/phone-api-service-pattern.md)
13. [references/source-map.md](references/source-map.md)
## Common Lifecycle Pattern
1. Provision account prerequisites (Zoom Phone license, admin setup, SMS readiness).
2. Create OAuth app and scopes in Marketplace.
3. Choose integration surface:
- Smart Embed (iframe + postMessage)
- REST + webhooks
- URI launch (`callto`, `tel`, `zoomphonecall`, `zoomphonesms`)
4. Capture real-time events (Smart Embed events and/or webhooks).
5. Persist call identifiers and correlate records (`call_id`, `call_history_uuid`, `call_element_id`).
6. Apply migration-safe data mapping (v1 -> v2 -> v3) and handle renamed fields.
7. Harden security (origin validation, webhook signature validation, least-privilege scopes).
## High-Level Scenarios
- CRM softphone pane using Smart Embed + contact search/match callbacks.
- Click-to-call from account/contact table via `zp-make-call`.
- Call disposition workflow using `zp-save-log-event` and custom notes page.
- SMS engagement workflow with `zoomphonesms://` and `zp-sms-log-event`.
- Real-time operational board driven by `phone.*` webhook events.
- Call analytics migration from legacy call logs to call history/call elements.
- Admin automation for user/auto-receptionist/call-queue call-handling settings.
See [scenarios/high-level-scenarios.md](scenarios/high-level-scenarios.md) for details.
## Chaining
- OAuth setup/token lifecycle: [../oauth/SKILL.md](../oauth/SKILL.md)
- Phone and account resources via REST: [../rest-api/SKILL.md](../rest-api/SKILL.md)
- Event delivery and signature validation: [../webhooks/SKILL.md](../webhooks/SKILL.md)
- Contact Center blended journey: [../contact-center/SKILL.md](../contact-center/SKILL.md)
## Environment Variables
- See [references/environment-variables.md](references/environment-variables.md) for standardized `.env` keys and where to find each value.
troubleshooting/common-issues.md
# Zoom Phone Common Issues
## Smart Embed event listener gets nothing
Checks:
- Iframe is loaded from `https://applications.zoom.us`.
- `onZoomPhoneIframeApiReady` sequence is respected.
- `postMessage` origin checks are correct.
- Approved domains configured in Zoom Phone Smart Embed app settings.
## OAuth works but API calls fail (401/403)
Checks:
- Required scopes are present and app was re-authorized.
- Access token is current (refresh flow works).
- Right app type and account context are used.
## Data fields missing after migration
Checks:
- Code expects old fields (`call_logs`, `call_path`) only.
- Endpoint path still points to legacy call log URLs.
- Webhook processor supports `call_element_id` fields.
## URI launch inconsistencies
Checks:
- Client is installed and signed in.
- Scheme is valid (`callto:`, `tel:`, `zoomphonecall://`, `zoomphonesms://`).
- Platform caveats are handled.
- Android caveat: docs explicitly note no `zoomphonecall`/`tel` support due to system limitations.
## Call handling API patch fails
Checks:
- `extensionId` target type is correct.
- Payload subsetting matches endpoint context.
- Phone numbers are E.164 where required.
- Enum/action values are valid for current API version.