A lightweight Zoom API client for Go. Endpoint coverage is minimal, but the existing services are designed to be easy to extend.
go get github.com/Hawkeye-Claims/go-zoomA lightweight CLI wrapper is included under cmd/go-zoom and uses only the Go standard library for command parsing and output formatting.
You can use the CLI in two ways:
Option 1: Install the prebuilt CLI
Prebuilt CLI binaries are attached to each GitHub release for Linux, macOS, and Windows on amd64 and arm64. Download a release and add it to your $PATH.
Option 2: Build from source
Build and run the CLI directly from the repository:
go run ./cmd/go-zoom <command> [subcommand] [flags]Or install it globally with:
go install github.com/Hawkeye-Claims/go-zoom/cmd/go-zoom@latestThe CLI exposes an auth command for SDK authentication options:
# Validate Server-to-Server OAuth credentials
go run ./cmd/go-zoom auth test --grant-type account_credentials
# Run an Authorization Code helper flow locally
go run ./cmd/go-zoom auth authorization-code \
--redirect-uri http://localhost:8080/oauth/callbackCredentials can be provided via flags or environment variables:
ZOOM_ACCOUNT_IDZOOM_CLIENT_IDZOOM_CLIENT_SECRET
The CLI covers users, meetings, and phone service groups.
Use go run ./cmd/go-zoom --help (and <command> --help) to see the full command list.
Below are a few common examples:
# Read a user
go run ./cmd/go-zoom users get --user-id me
# Create a meeting from inline JSON
go run ./cmd/go-zoom meetings create \
--user-id me \
--json '{"topic":"Project kickoff","type":2,"start_time":"2026-04-11T15:00:00Z","duration":30}'
# Create a user from a JSON file
go run ./cmd/go-zoom users create \
--action create \
--json-file /path/to/user.jsonget commands that support filtering accept --query-json and --query-json-file.
These use strict JSON decoding (unknown fields are rejected), just like create payloads.
Use this for backend services with a Server-to-Server OAuth app. This is the default grant type — no extra options are needed.
import (
"net/http"
"os"
"github.com/Hawkeye-Claims/go-zoom/zoom/client"
)
c, err := client.NewClient(
&http.Client{},
os.Getenv("ZOOM_ACCOUNT_ID"),
os.Getenv("ZOOM_CLIENT_ID"),
os.Getenv("ZOOM_CLIENT_SECRET"),
)Tokens are fetched and cached automatically. They are refreshed on expiry or when a 401 response is received.
Use this for user-facing apps where end users authorize your application via the Zoom OAuth flow. You must supply a redirect URI and register the two HTTP middleware handlers on your server.
c, err := client.NewClient(
&http.Client{},
os.Getenv("ZOOM_ACCOUNT_ID"),
os.Getenv("ZOOM_CLIENT_ID"),
os.Getenv("ZOOM_CLIENT_SECRET"),
client.WithGrantType("authorization_code"),
client.WithRedirectURI("https://yourapp.example.com/oauth/callback"),
)
if err != nil {
log.Fatal(err)
}
// Redirect users to Zoom's authorization page
http.Handle("/oauth/login", c.RequestAuthorization())
// Handle the callback from Zoom, exchange the code for a token
http.Handle("/oauth/callback", c.HandleOAuthCallback())RequestAuthorization redirects the incoming request to Zoom's authorization URL. HandleOAuthCallback exchanges the authorization code for an access token and stores it. After the callback completes, the client is ready to make API requests on behalf of the user. Refresh tokens are rotated automatically.
By default, tokens are stored in memory. For horizontally scaled deployments, implement the TokenMutex interface and inject it via WithToken:
c, err := client.NewClient(
&http.Client{},
os.Getenv("ZOOM_ACCOUNT_ID"),
os.Getenv("ZOOM_CLIENT_ID"),
os.Getenv("ZOOM_CLIENT_SECRET"),
client.WithToken(myRedisTokenStore),
)The TokenMutex interface:
type TokenMutex interface {
Lock(context.Context) error
Unlock(context.Context) error
Get(context.Context) (string, error)
GetRefreshToken(context.Context) (string, error)
Set(context.Context, string, time.Time) error
SetRefreshToken(context.Context, string) error
Clear(context.Context) error
}NewClient initializes c.Users and c.Meetings automatically. The Phone service tree must be initialized separately by calling NewPhoneService:
c, err := client.NewClient(...)
// Initialize Phone sub-services (CallHistory, Recordings, Settings, Users)
client.NewPhoneService(c)After calling NewPhoneService, the following are available: c.Phone.CallHistory, c.Phone.Recordings, c.Phone.Settings, and c.Phone.Users.
// Get a single user
users, _, err := c.Users.Get(ctx, client.WithUserId("me"))
// List all users (auto-paginated)
users, _, err := c.Users.Get(ctx, client.WithListUserQueryParameters(&client.ListUserQueryParameters{
Status: enums.ActiveUser,
}))
// Create a user
user, _, err := c.Users.Create(ctx, enums.Create, client.UserAttributes{...})
// Update a user
_, err := c.Users.Update(ctx, "userId", &client.UserUpdateAttributes{...})
// Delete a user
_, err := c.Users.Delete(ctx, "userId")// Get a single meeting
meetings, _, err := c.Meetings.Get(ctx, client.WithMeetingId("meetingId"))
// List meetings for a user (auto-paginated)
meetings, _, err := c.Meetings.Get(ctx, client.WithMeetingUserId("userId"))
// Create a meeting
meeting, _, err := c.Meetings.Create(ctx, "userId", client.MeetingAttributes{...})
// Update a meeting
_, err := c.Meetings.Update(ctx, meetingId, &client.MeetingUpdateAttributes{...})
// Delete a meeting
_, err := c.Meetings.Delete(ctx, meetingId)// Get AI-generated summaries for a meeting
summaries, _, err := c.Meetings.GetSummary(ctx, client.WithMeetingIdForSummary("meetingId"))
// Delete a meeting summary
_, err := c.Meetings.DeleteSummary(ctx, "meetingId")// Get account-wide call history (auto-paginated)
history, _, err := c.Phone.CallHistory.Get(ctx)
// Get call history for a specific user (auto-paginated)
history, _, err := c.Phone.CallHistory.Get(ctx,
client.WithUserIdForPhoneCallHistory("userId"),
client.WithPhoneCallHistoryQueryParameters(&client.PhoneCallHistoryQueryParameters{
From: "2024-01-01",
To: "2024-01-31",
}),
)
// Get a single call history record by UUID
history, _, err := c.Phone.CallHistory.Get(ctx, client.WithPhoneCallHistoryUUID("uuid"))
// Get a single call element
element, _, err := c.Phone.CallHistory.GetCallElement("callElementId")
// Get an AI call summary
summary, _, err := c.Phone.CallHistory.GetAICallSummary("userId", "aiCallSummaryId")
// Add a client code to a call log entry
_, err := c.Phone.CallHistory.AddClientCode("callLogId", "clientCode")
// Delete a user's call history entry
_, err := c.Phone.CallHistory.DeleteUserCallHistory("userId", "callLogId")// List call recordings for a user (auto-paginated)
recordings, _, err := c.Phone.Recordings.Get(ctx, client.WithRecordingUserId("userId"))
// Get recordings for a specific call
recordings, _, err := c.Phone.Recordings.Get(ctx, client.WithRecordingCallId("callId"))
// Download a recording to an io.Writer
_, err := c.Phone.Recordings.DownloadCallRecording(ctx, "fileId", w)
// Download a transcript
transcript, _, err := c.Phone.Recordings.DownloadCallTranscript(ctx, "recordingId")
// Enable or disable auto-delete for a recording
_, err := c.Phone.Recordings.EnableAutoDelete("recordingId")
_, err := c.Phone.Recordings.DisableAutoDelete("recordingId")
// Recover a deleted recording
_, err := c.Phone.Recordings.Recover(ctx, "recordingId")
// Delete a recording
_, err := c.Phone.Recordings.Delete(ctx, "recordingId")// Get account-level phone settings
settings, _, err := c.Phone.Settings.Get(ctx)
// Update account-level phone settings
_, err := c.Phone.Settings.Update(ctx, &client.SettingsAttributes{...})// List all phone users (auto-paginated)
users, _, err := c.Phone.Users.Get(ctx)
// Filter phone users
users, _, err := c.Phone.Users.Get(ctx,
client.WithPhoneUserQueryParameters(&client.PhoneUserQueryParameters{
Status: enums.ActiveUser,
}),
)
// Get a single phone user by ID
users, _, err := c.Phone.Users.Get(ctx, client.WithPhoneUserID("userId"))
// Get a phone user's profile settings
settings, _, err := c.Phone.Users.GetProfileSetting(ctx, "userId")Use server.NewWebhookServer to receive and process Zoom webhook events. All incoming requests are verified with HMAC-SHA256 against your webhook secret token before any handler is invoked. The endpoint.url_validation handshake required by Zoom is handled automatically.
import (
"fmt"
"os"
"github.com/Hawkeye-Claims/go-zoom/zoom/server"
)
meetingCh := make(chan server.MeetingEvent, 10)
userCh := make(chan server.UserEvent, 10)
ws := server.NewWebhookServer(
":8080",
"/zoom/webhook",
os.Getenv("ZOOM_WEBHOOK_SECRET"),
server.WithHandler("meeting.created", meetingCh),
server.WithHandler("user.created", userCh),
)
go ws.Start()
for evt := range meetingCh {
fmt.Println("New meeting:", evt.Object.Topic)
}WithHandler uses Go generics to route each event type to a typed channel. The payload is unmarshalled directly into the channel's element type — no type assertions needed.
Several event payload structs are provided out of the box:
| Type | Backing model | Fields |
|---|---|---|
server.MeetingEvent |
models.Meeting |
AccountId, Object, Operator, OperatorId, Operation |
server.UserEvent |
models.User |
AccountId, Object, Operator, OperatorId, CreationType |
server.AICallSummaryEvent |
models.AICallSummary |
AccountId, Object |
server.PhoneCallElementEvent |
[]models.CallElement |
AccountId, Object.CallElements, UserID |
server.PhoneCallHistoryEvent |
[]models.CallHistory |
AccountId, Object.CallLogs, UserID |
You are not limited to the built-in types. Any struct can be used as the payload generic for WithHandler. Define your own struct matching the payload field of the Zoom event you want to handle and pass a channel of that type:
type MyCustomPayload struct {
AccountId string `json:"account_id"`
// ... fields matching the Zoom event payload
}
ch := make(chan MyCustomPayload, 10)
server.WithHandler("recording.completed", ch)The full event envelope is defined as server.Notification[T] and is available if you need access to the top-level Event string or EventTs timestamp:
type Notification[T any] struct {
Event string `json:"event"`
EventTs int64 `json:"event_ts"`
Payload T `json:"payload"`
}Paginated endpoints follow next_page_token automatically and return the complete result set. No additional handling is required by the caller.