---
name: zoom-phone-oauth-app
description: The Zoom Phone layer on top of the shared `zoom-oauth-app` skill — the Zoom Phone license and account-plan prerequisite that makes every phone endpoint 403 without it, the `phone:` granular scope family and its admin/user split, the call-log APIs sunset in June 2025 and the call-history APIs that replaced them, recording and transcript scopes, and Zoom Phone's own rate-limit table. Use when asked to get OAuth credentials for Zoom Phone, add phone scopes to a Zoom Marketplace app, work out why Zoom Phone endpoints 403 for one customer, or decide which call-log generation to call. Read `zoom-oauth-app` first for the Marketplace mechanics; use that skill alone when the integration is Zoom meetings, users, groups or Team Chat rather than Phone.
---

# Zoom Phone OAuth2 App Registration

**Zoom Phone is not a separate developer portal, a separate app, or a separate credential.** It is a scope family on
the same Zoom Marketplace app, reached with the same client ID and the same one-hour tokens. There is nothing to
register here that `zoom-oauth-app` does not already cover.

What Zoom Phone adds is a **licensing precondition that no OAuth configuration can satisfy**: the customer's account
must be on a qualifying plan *and* carry a Zoom Phone license, or every `/v2/phone/**` call returns 403 with a
perfectly valid token. That, and a call-log API generation that was sunset in 2025, are the two things that cost
time.

## Built on: `zoom-oauth-app`

**Read `zoom-oauth-app` first, then this file.** It owns all the Marketplace mechanics, and this file does not
repeat them:

- Creating a **General app** (vs Server-to-Server OAuth, and why JWT apps are gone), and the admin-managed vs
  user-managed choice that decides which scopes exist.
- Redirect URL and the **OAuth allow list**, Strict Mode and Subdomain check, the platform's per-data-center callback
  list, loopback and PKCE rules.
- **Classic vs granular scopes**: the two vocabularies never coexist in one app, new apps get granular, the
  migration button, optional scopes and the `scope` / `optional_scope` / `include_granted_scopes` authorize
  parameters.
- The **two credential pairs** (development and production), the `api_url` region rule, one-hour access tokens, and
  90-day refresh tokens that **rotate on every refresh**.
- **Distribution and review**: private/beta install caps (10 account-level, 100 user-level, 4-week beta URLs), what
  Marketplace review asks for, the mandatory deauthorization endpoint, and account-wide plan-based rate limits.

If you are reading only this file, you are missing all of the above.

## Inputs to collect before you start

The base skill lists the app inputs. These are the Phone-specific ones:

| Input | Notes |
| --- | --- |
| **Does the customer's account hold a Zoom Phone license?** | Blocking — no scope or app setting substitutes for it (§1) |
| **What account plan?** | Pro / Business / Enterprise / Education gates differ per endpoint (§1) |
| **Account-wide phone data, or one user's?** | Decides `:admin` scopes and therefore admin-managed (§2) |
| **Are call recordings and transcripts in scope?** | Separate scopes, separate admin privilege, separate account settings (§4) |
| **Which call-log generation?** | The originals were sunset 2025-06-18 (§3) |

## Zoom Phone platform state (verified 2026-09-20 — re-verify before trusting)

- **Every Zoom Phone endpoint lists a Zoom Phone license as a prerequisite**, alongside an account-plan floor that
  varies by endpoint — commonly "A Business or Enterprise account", sometimes "A Pro or higher account plan", and for
  the dashboard call-log metrics "Business or Education account". The API reference states it plainly: "You'll
  receive a `403` error message if you have not set up Zoom Phone."
- **Granular `phone:` scopes are `phone:<action>:<data_claim>[:admin]`** — for example
  `phone:read:list_users:admin`, `phone:read:call_log:admin`, `phone:read:call_recording`. The classic equivalents
  are the macro scopes `phone:read`, `phone:read:admin`, `phone:write:admin`, plus the older
  `phone_call_log:read[:admin]` and `phone_recording:read[:admin]`.
- **The first-generation call-log APIs were sunset on 2025-06-18** — "Get account's call logs" and "Get call log
  details". The account, user, sync and delete call-log endpoints all now carry a deprecation marker, and the
  replacements live under call **history**.
- **Zoom Phone has its own rate-limit table**, separate from the general Zoom API one and with no Free tier: Light
  20/s (Pro) and 40/s (Business+), Medium 10/s and 20/s, Heavy 5/s and 10/s, Resource-intensive 5/min and 10/min,
  with a daily allowance of 15,000 (Pro) or 30,000 (Business+) shared between heavy and resource-intensive calls.
- **A separate Zoom Phone Master API exists** for master accounts managing sub-accounts, behind `:master` scopes that
  only an account owner can grant to an account-level app.

If the Phone docs do not look like this, stop and report what you actually see.

## 1. The license precondition

This is the whole difference between Zoom Phone and the rest of the Zoom API, and it is invisible from the app
configuration:

- The app can be perfectly registered, the scopes approved, the token valid, and **every phone call still 403s**
  because the authorizing account has no Zoom Phone license — or has one but is on a plan below the endpoint's
  floor.
- It is a **per-customer** condition, so a connector that works for nine customers can fail wholesale for the tenth,
  with nothing in your logs distinguishing it from a permissions bug.
- There is no remedy on your side. The answer is "your Zoom account needs a Zoom Phone license on a qualifying
  plan," and it is a purchase, not a setting.

Check this before scoping any Zoom Phone work: ask whether the *test* account has a Phone license. Without one you
cannot verify a single endpoint, and an app that passes review on meeting scopes tells you nothing about Phone.

## 2. Admin vs user scopes

The `:admin` suffix is doing more work here than usual:

- **`phone:...:admin` scopes read the whole account's phone data** — every user, every call, every recording. They
  require an **admin-managed (account-level) app**, and the person clicking Allow must be an account owner or admin.
  Several endpoints additionally demand "Account owner or a role with Zoom Phone management", which is a Zoom
  role-based-access setting on the customer's side, not a scope.
- **User-level `phone:` scopes** (no suffix) see only the authorizing user's own phone data, and those endpoints
  accept `me` in place of a user ID.
- Mixing them does not widen anything: a user-managed app cannot hold `:admin` scopes at all, and an account-level
  app whose installer is a plain user authorizes fine and then 403s on admin endpoints.

**The shared-access 403 lands squarely on Zoom Phone.** Zoom's list of paths that return "authenticated user has not
permitted access to the targeted resource" names `/v2/phone/**` (user-level) explicitly: if your app touches a user
other than the one who added it, and that user either lacks the privilege or has not granted your app shared access
permissions, you get a 403 you **cannot detect in advance**. See the base skill's §8.

## 3. Call logs: two generations, one set of scope names

The trap is that the old and new endpoints share granular scope names, so **the scope list does not tell you which
generation you are calling**.

- **Sunset 2025-06-18**: the account-wide call-log list and the call-log detail endpoint. The user-level call-log
  list, sync and delete endpoints are likewise marked deprecated.
- **Current**: the call-**history** endpoints — an account-wide list, a single-call lookup, user-level history, sync
  and delete, plus a client-code patch.
- Both generations sit behind `phone:read:list_call_logs:admin` (list) and `phone:read:call_log:admin` (single), with
  classic `phone:read:admin` / `phone_call_log:read:admin` as the pre-granular equivalents. So a scope audit will
  look clean on a connector still calling a dead endpoint.
- Separately, the **dashboard** call-log metrics endpoints share those same scope names again while returning
  monthly metrics filtered by date, site and MOS — a different shape of data for a different question.
- The structural point worth passing on: for inbound calls the list response is often only "a small fraction of how
  the call routes," because auto receptionists, phone menus, call queues and shared lines each add components. Full
  call accounting means a **second call per call** to the detail endpoint. Budget that against the rate limits in the
  platform-state section — the list endpoints are labelled HEAVY.

## 4. Recordings and transcripts

- Account-wide recording listing uses `phone:read:list_call_recordings:admin` (classic `phone_recording:read:admin`)
  and requires "A Pro or higher account plan", a Zoom Phone license, and **account owner or admin privileges**.
  Per-user recording listing has its own scope pair, and the per-recording fetch and download use
  `phone:read:call_recording[:admin]`.
- **Transcripts are a separate scope**, `phone:read:recording_transcript[:admin]`, and a separate download endpoint
  that 302s to a JSON transcript file. Documented failure codes cover a recording that was never transcribed, an
  admin who disabled transcription, and one still being processed — all of which are normal states, not bugs.
- **Recording access narrows below the scope.** One recording lookup notes that "the current recording must belong
  to the receiver and call queue for it to be available": holding the scope does not entitle a token to every
  recording that exists.
- **On consent:** Zoom Phone carries account-level recording-consent and notification settings — an explicit-consent
  ("Press 1") option and recording-start prompts, configurable separately for inbound and outbound calls, and
  lockable by an admin. These govern whether calls get recorded at all and what callers hear; they are **not** an
  extra OAuth consent your app grants or requests. I found no documented requirement for a second authorization step
  beyond the recording scopes themselves — but recording law is jurisdictional, and whether a customer may hand you
  call audio is a question for them and their counsel, not something to settle from the API docs.

## Product fact — what the Zoom Phone connector asks for

> **As of 2026-09-20, Unified.to's Zoom Phone connector requests** (read-only; no write scopes at all):
>
> - **Phone users:** `phone:read:list_users:admin`
> - **Calls:** `phone:read:call_log:admin`, `phone:read:list_call_logs:admin`
> - **Recordings:** `phone:read:list_call_recordings:admin`, `phone:read:call_recording:admin`,
>   `phone:read:recording_transcript:admin`
> - **Identity / login only:** `user_profile`, `user_info:read`, `user:read`
>
> Three things to raise with the connector's owner before configuring an app:
>
> 1. **Every phone scope carries `:admin`**, so this is an admin-managed app installed by an account owner or admin,
>    on an account with a Zoom Phone license. There is no user-managed variant.
> 2. **The identity/login scopes are classic-shaped** while the phone scopes are granular — and the two vocabularies
>    cannot coexist in one app (base skill, §5). Confirm which vocabulary the app is actually on.
> 3. **This connector is flagged as requiring a custom API domain (CNAME) for its OAuth callback**, unlike the main
>    Zoom connector. Settle the hostname before registering redirect URLs, because changing the callback host later
>    means re-doing the OAuth allow list.
>
> It is also configured for Zoom Phone's page-token pagination rather than the offset paging the general Zoom
> connector uses, and it reads the region base URL returned with the token.
>
> Scope sets change. This note is dated, not live; confirm with the connector's owner before submitting anything.

## Phone-specific end-to-end checks

On top of the base skill's authorize → refresh round trip:

1. Confirm the **test account holds a Zoom Phone license** before concluding anything from a 403.
2. Make one real call to the phone-user list — it returns only users assigned a Phone license, so an empty list on a
   licensed account is itself a finding.
3. Fetch call history, not the sunset call-log endpoints, and confirm you are on the current generation.
4. If recordings matter, fetch a recording **and** a transcript — transcripts fail independently of recordings.
5. Watch for `429` against the **Zoom Phone** table, not the general one; the shared heavy/resource-intensive daily
   cap is the one that bites a backfill.

| Symptom | Cause |
| --- | --- |
| Every `/v2/phone/**` call 403s, token is valid | No Zoom Phone license, or account plan below the endpoint's floor (§1) |
| Phone works for most customers, 403 for one | That account's licensing or plan — not your app (§1) |
| Auth succeeds, admin phone endpoints 403 | User-managed app, or the installer is not an account owner/admin (§2) |
| 403 `authenticated user has not permitted access to the targeted resource` | Shared access permissions on a user other than the installer (§2) |
| Call-log endpoint returns nothing / is gone | Calling the generation sunset on 2025-06-18 (§3) |
| Inbound calls look truncated or mis-attributed | List response holds only the top-level leg; the detail call carries the routing (§3) |
| Recording listed but transcript download errors | Never transcribed, transcription disabled by admin, or still processing (§4) |
| Scope held but a specific recording is unavailable | Recording ownership narrows access below the scope (§4) |
| `429` during a backfill | Phone's own rate-limit table and its shared heavy/resource-intensive daily cap |

## Stop and ask

Beyond the base skill's list, hand back to a human when:

- Nobody can confirm the customer's (or the test account's) **Zoom Phone license and plan** — register nothing and
  promise nothing until that is answered.
- The product needs **account-wide** phone data but the install story is individual users self-connecting: `:admin`
  scopes and an admin installer are not optional (§2).
- **Call recordings or transcripts** are in scope and nobody has established who is accountable for the legality of
  storing call audio and its retention (§4).
- A connector is still calling the **sunset call-log endpoints** — fixing that is a code change with a behaviour
  difference, not a scope change (§3).
- Master-account / sub-account phone data is wanted, which means `:master` scopes and an account owner's grant.
- The Zoom Phone docs do not match the **platform state** section above.

## References

Phone-specific only; the base skill carries the general Zoom OAuth and Marketplace references. Every URL verified to
return 200 on 2026-09-20.

- Zoom Phone developer overview — https://developers.zoom.us/docs/phone/
- Zoom Phone API reference (per-endpoint prerequisites, classic and granular scopes, rate-limit labels) — https://developers.zoom.us/docs/api/phone/
- Understand Zoom Phone call logs (the 2025-06-18 sunset notice and call-flow structure) — https://developers.zoom.us/docs/phone/understanding-call-logs/
- Zoom Phone webhook events — https://developers.zoom.us/docs/api/phone/events/
- Zoom Phone Master API — https://developers.zoom.us/docs/api/phone/ma/
- Rate limits, including the Zoom Phone table — https://developers.zoom.us/docs/api/rate-limits/
- Using Zoom APIs — shared access permissions and the `/v2/phone/**` 403 — https://developers.zoom.us/docs/api/using-zoom-apis/
