Zoom Collection
Zoom Collection Integration with Onna
Overview
This guide explains how to set up and interact with a Zoom collection through the Onna Platform API. You'll learn how to create a workspace and collection, authorize access to Zoom via OAuth2, list the users and channels available in the Zoom account, configure sync filters to control exactly which users are synchronized, start the sync, and clean up your environment afterward.
Create a workspace
A workspace is the main container for your project in Onna. Create one with a POST request.
curl --location --request POST 'https://api.onna.com/v1/workspaces' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"name": "My New Workspace",
"description": "This is a workspace for organizing Zoom data in Onna."
}'
Where:
nameis the name of the workspace.descriptionis a brief description of the workspace's purpose.
A successful response (201) will return:
{
"onna_id": "<WORKSPACE_ID>"
}
Create a Zoom Wallet
Zoom credentials are stored in a wallet that the collection references. Create an empty wallet first; the OAuth pairing step later populates it with the access token.
curl --location --request POST 'https://api.onna.com/v1/wallet' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"name": "Zoom",
"credential_type_name": "Datasource",
"config_type": "zoom"
}'
Where:
nameis a label for the wallet.credential_type_nameis the credential type. For Zoom, useDatasource.config_typeidentifies the service the wallet holds credentials for. For Zoom, usezoom.
A successful response (201) will return:
{
"onna_id": "<WALLET_ID>"
}
Create a Zoom Collection
Next, create a Zoom collection within the workspace you created. The collection is the container for the users and content you will sync from Zoom.
curl --location --request POST 'https://api.onna.com/v1/collections' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"name": "My Zoom Collection",
"onna_parent_id": "<WORKSPACE_ID>",
"type": "zoom",
"config_type": "zoom",
"wallet_credentials": "<WALLET_ID>"
}'
Where:
nameis the name of the collection.onna_parent_idis the ID of the workspace that this collection will belong to.typeis the type of collection being created. For Zoom, the type iszoom.config_typeis the configuration type. For Zoom, this is alsozoom.wallet_credentialsis the ID of the wallet you created for the Zoom service.
A successful response (201) will return:
{
"onna_id": "<COLLECTION_ID>",
"type": "zoom"
}
You might also want to implement a naming convention (like Collection-{WorkspaceName}-Zoom) for better organization.
Initiate OAuth2 Flow to Get an Authorization Code
To authorize access to Zoom, initiate the OAuth2 flow using the source-type authorization URL.
The response is a 302 redirect to the Zoom authorization URL. Open that URL in your browser to
log in and grant the application access to Zoom.
Unlike Miro or Box, Zoom OAuth does not go through Connector Hub. The same
GET /collections/authorization_url/zoom endpoint is used, but Zoom is routed internally to
canonical @datasourceAuthCode/zoom.
curl --location --request GET 'https://api.onna.com/v1/collections/authorization_url/zoom?redirect_to=http%3A%2F%2Flocalhost%3A9000%2Fcallback' \
--header 'Authorization: Bearer <token>'
Where:
redirect_tois the URL Zoom redirects to after authorization. It must be on your account's allowlist of valid collection redirect URLs.
Once you've authorized, you'll be redirected to the provided redirect_to URL with an authorization code.
http://localhost:9000/callback) for easier testing. Be sure to change this to a production-ready URL before going live.OAuth for legal holds
For preservation (legal hold) flows, pass scopes_type=preservation so Zoom requests the
preservation scopes during authorization:
curl --location --request GET 'https://api.onna.com/v1/collections/authorization_url/zoom?redirect_to=http%3A%2F%2Flocalhost%3A9000%2Fcallback&scopes_type=preservation' \
--header 'Authorization: Bearer <token>'
You can also pass an optional state query parameter to carry context through the OAuth flow.
Pair the Authorization Code with the Collection (Token Exchange)
After successful authorization, exchange the authorization code for an access token bound to the collection.
curl --location --request POST 'https://api.onna.com/v1/collections/<COLLECTION_ID>/pair_credentials?auth_code=<AUTH_CODE>' \
--header 'Authorization: Bearer <token>'
Where:
auth_codeis the authorization code you received during the authorization process.
A successful response returns 204 No Content.
Retrieve the List of Users
To retrieve the users available in the authorized Zoom account, send a GET request to the collection's /users endpoint.
curl --location --request GET 'https://api.onna.com/v1/collections/<COLLECTION_ID>/users?limit=100' \
--header 'Authorization: Bearer <token>'
The endpoint accepts two optional query parameters:
limitis the number of users to return per page. For Zoom the default and maximum is300; a value greater than300is clamped to300.offsetis the pagination cursor. To fetch the next page, pass the value from the previous response'snext_page_tokenfield. Omit it for the first page.
Example user response:
{
"results": [
{
"id": "zoom-user-1",
"data": {
"id": "zoom-user-1",
"first_name": "Lisa",
"last_name": "Simpson",
"email": "lisa@onnaqa.com"
}
},
{
"id": "zoom-user-2",
"data": {
"id": "zoom-user-2",
"first_name": "Bart",
"last_name": "Simpson",
"email": "bart@onnaqa.com"
}
}
],
"next_page_token": null
}
Where:
idis the unique identifier of the user. Use this value in theselectedorexcludedarrays when configuring sync filters.datacontains the user profile fields returned by Zoom (such asfirst_name,last_name, andemail).next_page_tokenis the cursor for the next page of results, ornullwhen there are no more pages. To page through all users, pass this value as theoffsetquery parameter on the next request and repeat until it isnull.
Retrieve the List of Channels
To retrieve the channels for a Zoom user, send a GET request to the collection's /channels endpoint.
curl --location --request GET 'https://api.onna.com/v1/collections/<COLLECTION_ID>/channels?user=<USER_ID>&limit=100' \
--header 'Authorization: Bearer <token>'
The endpoint accepts these query parameters:
useris the Zoom user ID whose channels you want to list. This parameter is required.limitis the number of channels to return per page. For Zoom the default and maximum is300; a value greater than300is clamped to300.offsetis the pagination cursor. To fetch the next page, pass the value from the previous response'snext_page_tokenfield. Omit it for the first page.
Example channel response:
{
"results": [
{
"id": "channel-1",
"name": "General",
"is_private": false
},
{
"id": "channel-2",
"name": "Project Updates",
"is_private": true
}
],
"next_page_token": null
}
Where:
idis the unique identifier of the channel.nameis the channel display name.is_privateindicates whether the channel is private.next_page_tokenis the cursor for the next page of results, ornullwhen there are no more pages.
Configure Sync Filters
Sync filters control what is synchronized. For Zoom, the sync_filters object uses three keys that match the Onna UI (Select content screen):
account_owners: which Zoom account owners (users) to sync. Use theidvalues from the/usersendpoint.content_selections: meeting content types to sync (the Meetings section in the UI).chats: one-to-one chats and channels (the Chat section in the UI). Omit this key entirely if neither one-to-one chats nor channels are selected.
Update the collection with the selected configuration by sending a PATCH request.
Content types
The Onna UI Zoom collection: Select content screen maps to sync_filters as follows.
Meetings (content_selections)
| UI label | API key | Description |
|---|---|---|
| Chats | chat | In-meeting chat messages |
| Audio | audio | Meeting audio recordings |
| Video | video | Meeting video recordings |
| Transcripts | transcript | Meeting transcripts |
| AI Meeting Notes | AI-meeting-notes | Zoom AI Companion meeting notes |
Each type is set in content_selections.selected as a single-key object, for example { "chat": true } or { "transcript": false }.
Chat (chats)
| UI label | API key | Description |
|---|---|---|
| Chats | one-to-one | One-to-one Zoom chat messages |
| Channels | channels | Zoom team chat channels. Use all_selected, selected, and excluded to pick specific channels (IDs from /channels). |
Example structure for both chat options enabled with all channels:
"chats": {
"all_selected": true,
"selected": [
{ "one-to-one": true },
{
"channels": {
"all_selected": true,
"selected": [],
"excluded": []
}
}
]
}
Sync specific users with all content types
curl --location --request PATCH 'https://api.onna.com/v1/collections/<COLLECTION_ID>' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"sync_status": "pending",
"type_sync": "one",
"sync_filters": {
"account_owners": {
"all_selected": false,
"selected": [
{ "id": "zoom-user-1" }
],
"excluded": []
},
"content_selections": {
"all_selected": true,
"selected": [
{ "chat": true },
{ "audio": true },
{ "video": true },
{ "transcript": true },
{ "AI-meeting-notes": true }
]
},
"chats": {
"all_selected": true,
"selected": [
{ "one-to-one": true },
{
"channels": {
"all_selected": true,
"selected": [],
"excluded": []
}
}
]
}
}
}'
Sync all users
Set all_selected to true under account_owners with an empty selected array to sync every user in the Zoom account.
curl --location --request PATCH 'https://api.onna.com/v1/collections/<COLLECTION_ID>' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"sync_status": "pending",
"type_sync": "one",
"sync_filters": {
"account_owners": {
"all_selected": true,
"selected": [],
"excluded": []
},
"content_selections": {
"all_selected": true,
"selected": [
{ "chat": true },
{ "audio": true },
{ "video": true },
{ "transcript": true },
{ "AI-meeting-notes": true }
]
},
"chats": {
"all_selected": true,
"selected": [
{ "one-to-one": true },
{
"channels": {
"all_selected": true,
"selected": [],
"excluded": []
}
}
]
}
}
}'
/users and /channels endpoints help you discover IDs for account_owners and channel selection. The sync filter keys are account_owners, content_selections, and chats, not users and channels.Filter fields
The top-level request fields are:
| Field | Type | Description |
|---|---|---|
sync_status | String | Sync lifecycle state. Set to "pending" to queue a sync after filters are saved. |
sync_filters | Object | Zoom sync filter configuration (see below). |
type_sync | String | Sync type. Supported values: "one", "arch", "auto". |
The sync_filters object contains:
| Key | Description |
|---|---|
account_owners | User selection (before Select content in the UI). Same all_selected / selected / excluded structure. Each selected entry is { "id": "<user_id>" } from /users. |
content_selections | Meetings toggles. selected is an array of objects, each with one key: chat, audio, video, transcript, or AI-meeting-notes. |
chats | Chat toggles. selected contains { "one-to-one": true/false } and a nested channels object with all_selected, selected, and excluded. |
A successful PATCH returns 204 No Content, indicating the filter configuration was saved.
Update Existing Filters
To change an existing filter configuration (for example, to add or remove users),
issue another PATCH to the same collection endpoint with the full updated sync_filters object.
The new value replaces the previous one.
sync_filters object on update. Fields that are omitted are treated as removed, not merged with the previous value.Start the Sync
Once the filters are saved, initiate the sync process.
curl --location --request POST 'https://api.onna.com/v1/collections/<COLLECTION_ID>/start' \
--header 'Authorization: Bearer <token>'
A successful response (HTTP status code 200) indicates that the sync process has begun. Only the users allowed by the configured filters are pulled into the collection.
Simulate Token Expiry and Refresh the Token
If the access token expires, you can refresh it by using the refresh token.
curl --location --request POST 'https://api.onna.com/v1/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<client_id>' \
--data-urlencode 'client_secret=<client_secret>' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=<refresh_token>'
This will return a new access_token and refresh_token.
{
"access_token": "<new_access_token>",
"token_type": "bearer",
"expires_in": 86399
}
Clean Up
Once you are done, you can clean up by deleting both the collection and the workspace.
curl --location --request DELETE 'https://api.onna.com/v1/collections/<COLLECTION_ID>' \
--header 'Authorization: Bearer <token>'
curl --location --request DELETE 'https://api.onna.com/v1/workspaces/<WORKSPACE_ID>' \
--header 'Authorization: Bearer <token>'
Note: Deleting a collection or workspace is permanent and cannot be undone.