Zoom Meeting Automation
Centaur ships Zoom credential management, but it does not prescribe a meeting scheduler. Scheduling policy is deployment-specific: organizer selection, calendar ownership, attendee approval, recording defaults, retention, and where transcripts may be published all belong in your overlay.
This recipe shows how to combine a Console-managed Zoom token with a small tool and a durable workflow. Extend it with Google Calendar or another calendar provider if your deployment also needs availability and invitations.
1. Connect a Zoom account
Create a user-managed General App in the Zoom App Marketplace. Configure the callback URL as:
<CENTAUR_CONSOLE_PUBLIC_URL>/oauth/<slug>/callbackAdd only the scopes required by your implementation. A scheduler that creates meetings and later reads recordings commonly needs the user-level equivalents of:
user:read:userfor Centaur's/users/meidentity lookup (Centaur always requests this required scope);meeting:writeforPOST /users/me/meetings;recording:readif the workflow retrieves cloud-recording metadata or transcripts.
Zoom also offers granular scopes. Use the scopes shown for the exact endpoints in the current Zoom API reference, and add the same values to the OAuth app's allowed scopes in Centaur.
In the Centaur Console, open OAuth Apps, create an app with provider
zoom, and enter the Zoom client ID, client secret, and allowed scopes. Open
the app's consent link and authorize the Zoom user that should own meetings.
The Console exchanges the code, stores the rotating refresh-token family, and
creates a grantable wrapper secret. That wrapper injects the current access
token into requests to api.zoom.us; the tool does not need to declare or
handle the credential itself.
2. Add an overlay tool
Put the integration in your overlay rather than the base Centaur repo:
centaur-overlay/
├── tools/
│ └── zoom-scheduler/
│ ├── __init__.py
│ ├── client.py
│ ├── cli.py
│ └── pyproject.toml
└── workflows/
└── schedule_zoom_meeting.pyThe tool has no credential declaration. The Console-created wrapper owns the broker relationship, Zoom host rule, and bearer-header injection; declaring a second secret here would create a separate credential instead of using that wrapper.
[project]
name = "zoom-scheduler"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["httpx>=0.27.0", "typer>=0.12.0"]
[project.scripts]
zoom-scheduler = "zoom_scheduler.cli:app"
[tool.hatch.build.targets.wheel]
packages = ["."]
[tool.hatch.build.targets.wheel.sources]
"." = "zoom_scheduler"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.centaur]
module = "client.py"
secrets = []The client sends no authorization header. When the wrapper is granted to the
workflow principal, iron-proxy injects the current bearer token on the
allowed Zoom host:
import httpx
class ZoomScheduler:
def create_meeting(self, topic: str, start_time: str, duration: int) -> dict:
response = httpx.post(
"https://api.zoom.us/v2/users/me/meetings",
json={
"topic": topic,
"type": 2,
"start_time": start_time,
"duration": duration,
},
timeout=30,
)
response.raise_for_status()
meeting = response.json()
return {
"id": str(meeting["id"]),
"join_url": meeting["join_url"],
}
def _client() -> ZoomScheduler:
return ZoomScheduler()Keep the CLI wrapper thin and return only fields an agent or workflow needs.
Do not return start_url, authorization headers, signed download URLs, or raw
provider error bodies.
3. Put writes behind a durable workflow
Meeting creation is an external side effect. Give each requested occurrence a
stable business key and checkpoint the create call with ctx.step(...):
from dataclasses import dataclass
from api.workflow_engine import WorkflowContext
WORKFLOW_NAME = "schedule_zoom_meeting"
WORKFLOW_PRINCIPAL = True
@dataclass
class Input:
occurrence_key: str
topic: str
start_time: str
duration: int
async def handler(inp: Input, ctx: WorkflowContext) -> dict:
return await ctx.step(
f"create-zoom-meeting:{inp.occurrence_key}",
lambda: ctx.call_tool(
"zoom-scheduler",
"create_meeting",
{
"topic": inp.topic,
"start_time": inp.start_time,
"duration": inp.duration,
},
),
)For stronger recovery, persist occurrence_key, the Zoom meeting ID, and the
desired meeting fields in deployment-owned storage. Before retrying a create,
reconcile that record with Zoom. Use separate stable step names for calendar
creation, Zoom creation, invitation updates, and post-meeting processing so a
restart does not repeat completed provider writes.
4. Grant and verify access
Grant the Zoom wrapper secret directly to the workflow principal. The tool has no generated credential role because its manifest intentionally declares no secret. Do not make a scheduling credential a default role for all agent sessions.
After the overlay is deployed, verify:
centaur-tools listincludeszoom-schedulerin a fresh workflow sandbox.- The workflow principal can create one disposable meeting.
- The same call from an ungranted principal reaches Zoom without an authorization header and is rejected.
- Re-running the same occurrence key does not create a second meeting.
- Re-consent or token refresh updates the brokered credential without exposing the refresh token to the sandbox.
If you add recording or transcript retrieval, treat Zoom webhook delivery as a hint rather than the only source of truth: checkpoint the event, then reconcile the recording state through the API. Bound downloaded content and keep signed URLs, tokens, participant data, and provider response bodies out of logs and workflow results.