Skip to content

feat(calendar): add --with-zoom / --regenerate-zoom / --remove-zoom (parity with --with-meet) #589

@alexisperumal

Description

@alexisperumal

Summary

Add --with-zoom / --regenerate-zoom / --remove-zoom to gog calendar create and gog calendar update, with the same ergonomics as the existing --with-meet family (#538, #576). Plus an optional gog zoom meeting * standalone surface for cases where the agent needs to manage Zoom independently of a Calendar event.

Motivation

gog has excellent first-party Google Meet conferencing today via --with-meet. The most common gap I've heard from agent-facing users is Zoom, because many external clients explicitly request Zoom over Meet — particularly in consulting / client-services contexts.

Concrete use case: we run a fleet of OpenClaw agents that schedule meetings on behalf of principal users. Today an agent can attach Google Meet links via gog in one tool call. When a client requests Zoom, the agent has no programmatic surface — the Zoom for Google Workspace add-on is UI-only and can't be triggered via the Calendar API's conferenceData.createRequest (or at least, not reliably across third-party add-ons).

Ecosystem state — confirmed there's no existing solution

I scrubbed before opening this:

  • gogcli: 0 issues, 0 PRs mention zoom or conference. README lists Meet but not Zoom. internal/cmd/calendar_build.go has buildConferenceData(withMeet bool) hardcoded to Type: "hangoutsMeet" with no other path.
  • openclaw org: 0 Zoom-related plugins.
  • Wider OC community: two adjacent-but-non-overlapping plugins exist:

The "agent schedules a Zoom meeting and attaches it to a Google Calendar event" niche is uncovered.

Concrete target shape (captured from real Calendar event)

To make this implementable rather than abstract, I installed the Zoom for Google Workspace add-on on a test Google account (authenticated against a Pro-tier Zoom account), created a meeting via the Calendar UI's "Zoom Meeting" ADD-ON picker, and dumped the result via gog calendar raw. Sanitized:

{
  "conferenceData": {
    "conferenceId": "1234567890",
    "conferenceSolution": {
      "iconUri": "https://lh3.googleusercontent.com/pw/AM-JKLU.../s128-no",
      "key": { "type": "addOn" },
      "name": "Zoom Meeting"
    },
    "entryPoints": [
      {
        "entryPointType": "video",
        "label": "us06web.zoom.us/j/1234567890?pwd=REDACTED_PWD_TOKEN&jst=2",
        "meetingCode": "1234567890",
        "passcode": "000000",
        "uri": "https://us06web.zoom.us/j/1234567890?pwd=REDACTED_PWD_TOKEN&jst=2"
      },
      {
        "entryPointType": "phone",
        "label": "+1 312-626-6799",
        "passcode": "000000",
        "regionCode": "US",
        "uri": "tel:+13126266799,,1234567890#"
      },
      {
        "entryPointType": "sip",
        "label": "1234567890@zoomcrc.com",
        "passcode": "000000",
        "uri": "sip:1234567890@zoomcrc.com"
      },
      {
        "entryPointType": "more",
        "uri": "https://www.google.com/url?q=https://applications.zoom.us/addon/invitation/detail?..."
      }
    ],
    "notes": "Meeting host: <a href=\"mailto:host@example.com\">host@example.com</a><br /><br />Join Zoom Meeting: <br /><a href=\"https://us06web.zoom.us/j/1234567890?pwd=REDACTED_PWD_TOKEN&amp;jst=2\">https://us06web.zoom.us/j/1234567890?pwd=REDACTED_PWD_TOKEN&amp;jst=2</a><br /><br />Meeting agenda: <br /><a href=\"https://docs.zoom.us/agenda/doc/REDACTED?from=gsuite\">...</a><br /><br />Chat with Everyone: <br /><a href=\"https://us06web.zoom.us/launch/jc/1234567890\">...</a>",
    "parameters": {
      "addOnParameters": {
        "parameters": {
          "creatorUserId": "REDACTED_ZOOM_USER_ID",
          "meetingType": "2",
          "meetingUuid": "REDACTED_UUID==",
          "originalEventId": "REDACTED_EVENT_ID",
          "realMeetingId": "1234567890",
          "scriptId": "REDACTED_APPS_SCRIPT_ID"
        }
      }
    }
  },
  "extendedProperties": {
    "shared": {
      "meetingId": "1234567890",
      "meetingParams": "{\"topic\":\"...\",\"type\":2,\"start_time\":\"...\",\"duration\":60,\"timezone\":\"America/Chicago\",\"invitees_hash\":\"1B2M2Y8AsgTpgAmY7PhCfg==\",\"all_day\":false}",
      "zmMeetingNum": "1234567890"
    }
  }
}

Surprising findings from this dump (worth highlighting)

  1. conferenceSolution.key.type is literally "addOn" — the generic legacy string, not a Zoom-specific registered identifier. Good news: gog doesn't need to know any Zoom-specific magic strings to populate this field. Just "addOn" plus the human-facing name / iconUri.
  2. Rich text lives in conferenceData.notes (HTML allowed), not in the event description field. The event has no description set at all in the dump. Implementations should populate notes, not description.
  3. parameters.addOnParameters.parameters contains add-on-internal Apps Script state (scriptId, creatorUserId, meetingUuid, etc.). It's currently unclear whether Google validates this server-side when a non-add-on caller writes key.type = "addOn" — see open question feat(calendar): add respond, search, colors, time, conflicts commands #3 below.
  4. No signature field — Google doesn't sign third-party conference data here.
  5. extendedProperties.shared contains add-on internal bookkeeping. Probably not required for a third-party tool to populate, but worth a test.

Proposed surface

Tier 1 — Core (mirror --with-meet ergonomics)

# Create event + Zoom meeting in one call (default flow)
gog calendar create <calId> --summary "..." --from "..." --to "..." \
  --with-zoom --attendees "alice@x.com,bob@y.com"

# Idempotent attach Zoom to existing event (matches v0.16.0 --with-meet idempotency)
gog calendar update <calId> <eventId> --with-zoom

# Explicit replace (matches --regenerate-meet semantics; also cancels old Zoom meeting on Zoom side)
gog calendar update <calId> <eventId> --regenerate-zoom

# Remove Zoom from event + cancel on Zoom side
gog calendar update <calId> <eventId> --remove-zoom

Optional flags worth considering:

  • --zoom-no-notes — skip the conferenceData.notes HTML block (some users only want the structured conference card, not the duplicated link block).
  • --zoom-host <email> — schedule under a specific Zoom user (if the gog Zoom credentials have access to multiple users, e.g., in a Zoom Workspace plan).

Incidental gap noticed: there's currently no --remove-meet either. If you'd like Meet/Zoom parity, the same PR could add --remove-meet for symmetry.

Tier 2 — Standalone Zoom surface (nice-to-have; could be follow-up PR)

gog zoom meeting create [--topic ...] [--start ...] [--duration ...] \
  [--type scheduled|instant|recurring] [--password ...]
gog zoom meeting get <id>
gog zoom meeting list [--type scheduled|live|upcoming|previous_meetings]
gog zoom meeting update <id> [--topic ...] [--start ...] [--duration ...]
gog zoom meeting delete <id> [--notify-attendees]

Useful when the calendar event was created by someone else (or doesn't exist yet) and an agent needs to manage just the Zoom side.

Tier 3 — Explicit out-of-scope (setting expectations)

  • Webinars (separate Zoom product)
  • Recording configuration, waiting room, alternative hosts, breakout rooms, registration
  • Other conferencing providers (Webex, Teams) — pattern generalizes but solve Zoom first

Auth model

Zoom Server-to-Server OAuth (the modern path; replaces deprecated JWT; requires Zoom Pro tier or higher on the account that owns the OAuth app). Suggested layout mirroring existing gog auth:

  • gog zoom auth setup / gog zoom auth doctor / gog zoom auth tokens
  • Credential storage at ~/.config/gogcli/zoom/ parallel to Google OAuth keyring
  • Profile / alias support to match --account ergonomics
  • Env-var overrides for CI/container deployment:
    • GOGCLI_ZOOM_ACCOUNT_ID
    • GOGCLI_ZOOM_CLIENT_ID
    • GOGCLI_ZOOM_CLIENT_SECRET

Required Zoom OAuth scopes for the surface above:

  • meeting:write:admin (create/update/delete meetings)
  • meeting:read:admin (get meeting info)
  • user:read:admin (resolve user IDs, for --zoom-host and list operations)

Open questions for you

  1. Concrete vs. generic flags. Prefer --with-zoom / --regenerate-zoom (specific, discoverable, mirrors --with-meet) OR --conference-provider zoom / --regenerate-conference (extensible to Webex/Teams later, slightly less discoverable)? My soft preference is concrete-specific based on the --with-meet precedent, but you may have a stronger view on extensibility.

  2. Implementation architecture. Two viable paths to attach Zoom conferenceData:

    • (a) Direct Zoom API write. gog has its own Zoom OAuth credentials → creates the meeting via Zoom API → writes the resulting data to conferenceData.entryPoints + conferenceSolution.key.type = "addOn" + name = "Zoom Meeting" + iconUri. No dependency on the Zoom add-on being installed in the user's Calendar.
    • (b) Calendar API createRequest passthrough. gog asks the Calendar API to invoke the Zoom add-on via conferenceData.createRequest. Simpler in code but requires (i) Zoom add-on installed and authenticated in the user's Calendar account, (ii) the add-on implementing the necessary createConference callback per Google's Conference Add-on spec — and many third-party add-ons don't.

    My read: (a) is the safer bet because it works regardless of whether the user has the add-on installed, and gives us control over the auth and meeting settings. But you may know more about (b) viability than I do.

  3. addOnParameters server-side validation. As called out above, the Zoom add-on populates conferenceData.parameters.addOnParameters.parameters with Apps Script-side state (scriptId, creatorUserId, meetingUuid, etc.). Whether Google's Calendar API requires these fields when a non-add-on caller writes key.type = "addOn" conferenceData is the one unknown that'll need testing during implementation. Possible fallbacks if Google rejects:

    • Populate plausible / NULL values
    • Use a different key.type (no documented alternative cleanly fits Zoom)
    • Accept degraded rendering (no Zoom branding on the event card)

Pattern precedent

References for design / scope alignment:

Help offered

Happy to test a release candidate against our OpenClaw agent fleet — real-world use case (agents scheduling Zoom on behalf of principals) means we'll exercise the auth refresh path, error handling, and edge cases quickly. Also happy to provide the full unsanitized conferenceData JSON via DM if useful for implementation.

Thanks for considering!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions