Microsoft Teams Enterprise Collection
Microsoft Teams Enterprise Collection Integration with Onna
Overview
This guide explains how to set up and interact with a Microsoft Teams (emsteams) collection through the Onna Platform API. You'll learn how to create a workspace and collection, authorize access to Microsoft 365 via OAuth2, list tenant users, teams, and channels, configure sync filters to control exactly which teams, channels, and user chats are synchronized, start the sync, and clean up your environment afterward.
Teams sync filters support two independent content types that can be combined:
- Channels - messages from standard and private channels within Microsoft Teams teams.
- Chats - one-on-one and group chat messages for specific custodian users.
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 Microsoft Teams 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 Teams Collection
Next, create a Teams collection within the workspace you created. The collection is the container for the teams, channels, and chats you will sync from Microsoft 365.
curl --location --request POST 'https://api.onna.com/v1/collections' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"name": "My Microsoft Teams Enterprise Collection",
"onna_parent_id": "<WORKSPACE_ID>",
"type": "emsteams"
}'
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 Microsoft Teams, the type isemsteams.
A successful response (201) will return:
{
"onna_id": "<COLLECTION_ID>",
"type": "emsteams"
}
You might also want to implement a naming convention (like Collection-{WorkspaceName}-Teams) for better organization.
Initiate OAuth2 Flow to Get an Authorization Code
To authorize access to the Microsoft 365 tenant, initiate the OAuth2 flow. It will return an authorization URL. Open this URL in your browser to log in and grant the application access.
curl --location --request GET 'https://api.onna.com/v1/collections/authorization_url/emsteams?redirect_to=http%3A%2F%2Flocalhost%3A9000%2Fcallback' \
--header 'Authorization: Bearer <token>'
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.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>' \
--header 'Content-Type: application/x-www-form-urlencoded'
Where:
auth_codeis the authorization code you received during the authorization process.
Retrieve the List of Tenant Users
To retrieve the users available in the authorized Microsoft 365 tenant, send a GET request to the collection's /users endpoint. This is useful for identifying user IDs when configuring custodian-level chat sync filters.
curl --location --request GET 'https://api.onna.com/v1/collections/<COLLECTION_ID>/users' \
--header 'Authorization: Bearer <token>'
Example user response:
{
"entries": [
{
"id": "f8a9c3b1-1234-4abc-9def-0987654321ab",
"display_name": "Lisa Simpson",
"email": "lisa@onnaqa.com"
},
{
"id": "d4c7b8e2-5678-4fff-aaaa-1122334455cc",
"display_name": "Bart Simpson",
"email": "bart@onnaqa.com"
}
],
"limit": 1000,
"next_marker": "<next_marker>"
}
Where:
idis the unique Microsoft 365 object ID of the user. Use this as the filter key when configuring per-user sync filters.display_nameis the user's display name.emailis the user's primary email address (userPrincipalName).
Configure Sync Filters
Sync filters control which content is pulled into the collection. Teams sync filters use the following top-level keys inside the sync_filters object:
types- selects which content types to sync (channels,chats, or both).teams- selects which teams to include or exclude (only applies when syncing channels without auserskey present).- A per-team key (
<team_id>) - selects which channels to include or exclude within a specific team. users- selects which custodian users to include. When present, activates custodian mode for both channel and chat syncing, and the top-levelteamskey is ignored.- A per-user key (
<user_id>) - controls which teams are included for that user when syncing channels in custodian mode.
Update the collection with the selected configuration by sending a PATCH request.
Sync all channels across all teams
Use all_selected: true under both teams and the types entry for channels to capture every channel in the tenant.
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",
"sync_filters": {
"types": {
"all_selected": false,
"selected": [{ "id": "channels" }]
},
"teams": {
"all_selected": true,
"selected": [],
"excluded": []
}
}
}'
Sync specific teams only
Set all_selected to false under teams and list the teams to include in selected.
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",
"sync_filters": {
"types": {
"all_selected": false,
"selected": [{ "id": "channels" }]
},
"teams": {
"all_selected": false,
"selected": [
{ "id": "<team_id_1>" },
{ "id": "<team_id_2>" }
],
"excluded": []
}
}
}'
Sync specific channels within a team
Combine a teams entry with a per-team key to select individual channels. The per-team key uses the team ID and contains a standard all_selected / selected / excluded filter.
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",
"sync_filters": {
"types": {
"all_selected": false,
"selected": [{ "id": "channels" }]
},
"teams": {
"all_selected": false,
"selected": [
{ "id": "<team_id_1>" }
],
"excluded": []
},
"<team_id_1>": {
"all_selected": false,
"selected": [
{ "id": "<channel_id_1>" },
{ "id": "<channel_id_2>" }
],
"excluded": []
}
}
}'
Sync chats for specific custodian users
Set types to include chats and list the users whose chats should be collected under users.
All chats belonging to the listed users are included automatically.
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",
"sync_filters": {
"types": {
"all_selected": false,
"selected": [{ "id": "chats" }]
},
"users": {
"all_selected": false,
"selected": [
{ "id": "f8a9c3b1-1234-4abc-9def-0987654321ab" },
{ "id": "d4c7b8e2-5678-4fff-aaaa-1122334455cc" }
],
"excluded": []
}
}
}'
Sync both channels and chats
Include both channels and chats in the types filter. Use the users key to list custodian users,
and a per-user key with all_selected: true to include all teams that user belongs to for channel syncing.
Note that the top-level teams key is ignored when users is present; team scoping is controlled
exclusively via per-user keys in this mode.
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",
"sync_filters": {
"types": {
"all_selected": false,
"selected": [
{ "id": "channels" },
{ "id": "chats" }
]
},
"users": {
"all_selected": false,
"selected": [
{ "id": "f8a9c3b1-1234-4abc-9def-0987654321ab" }
],
"excluded": []
},
"f8a9c3b1-1234-4abc-9def-0987654321ab": {
"all_selected": true
}
}
}'
Filter fields
The top-level request fields are the same as any collection update:
| Field | Type | Description |
|---|---|---|
sync_status | String | Sync lifecycle state. Set to "pending" to queue a sync after filters are saved. |
sync_filters | Object | Teams sync filter configuration (see below). |
type_sync | String | Sync type. Supported values: "one", "arch", "auto". |
The types object inside sync_filters controls which content types are collected:
| Field | Type | Description |
|---|---|---|
all_selected | Boolean | If true, all supported content types are collected. |
selected | Array | Content types to collect. Supported values: { "id": "channels" } and { "id": "chats" }. |
excluded | Array | Content types to exclude when all_selected is true. |
The teams object inside sync_filters controls team selection (only applies when no users key is present):
| Field | Type | Description |
|---|---|---|
all_selected | Boolean | If true, all teams in the tenant are included. When false, only teams listed in selected are included. |
selected | Array | Teams to include when all_selected is false. Each entry is { "id": "<team_id>" }. |
excluded | Array | Teams to exclude when all_selected is true. Each entry is { "id": "<team_id>" }. |
Each per-team entry uses the team ID as its key and contains a standard filter object that controls channel selection within that team:
| Field | Type | Description |
|---|---|---|
all_selected | Boolean | If true, all channels in the team are included. |
selected | Array | Channels to include when all_selected is false. Each entry is { "id": "<channel_id>" }. |
excluded | Array | Channels to exclude when all_selected is true. Each entry is { "id": "<channel_id>" }. |
The users object inside sync_filters activates custodian mode. When present, the top-level teams key
is ignored and team selection is driven by per-user keys. users applies to both chat syncing and
custodian channel syncing:
| Field | Type | Description |
|---|---|---|
all_selected | Boolean | If true, all users in the tenant are included. When false, only users listed in selected are included. |
selected | Array | Users to include when all_selected is false. Each entry is { "id": "<user_id>" }. |
excluded | Array | Users to exclude when all_selected is true. Each entry is { "id": "<user_id>" }. |
Each per-user entry uses the user's Microsoft 365 object ID as its key and controls which teams are included for that user when channel syncing is active in custodian mode:
| Field | Type | Description |
|---|---|---|
all_selected | Boolean | If true, all teams the user belongs to are eligible for channel syncing. If false, only teams listed in selected are included. |
selected | Array | Teams to include for this user when all_selected is false. Each entry is { "id": "<team_id>" }. |
excluded | Array | Teams to exclude for this user when all_selected is true. Each entry is { "id": "<team_id>" }. |
A successful PATCH returns 200 OK with no body, indicating the filter configuration was saved.
Update Existing Filters
To change an existing filter configuration (for example, to add a new team or include an additional custodian
user), issue another PATCH to the same collection endpoint with the full updated sync_filters object.
The new value replaces the previous one.
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",
"sync_filters": {
"types": {
"all_selected": false,
"selected": [{ "id": "channels" }]
},
"teams": {
"all_selected": false,
"selected": [
{ "id": "<team_id_1>" },
{ "id": "<team_id_2>" },
{ "id": "<team_id_3>" }
],
"excluded": []
}
}
}'
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>' \
--header 'Content-Type: application/json'
A successful response (HTTP status code 200) indicates that the sync process has begun. Only the teams, channels, and user chats 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.