This is the partner integration surface, the same API the Zapier app is built on. The base URL is https://teams-app.maestrolabs.com and every path below sits under /integrations/partners/.
Authentication
Integrations authenticate with OAuth 2.0, authorization code with PKCE. PKCE is required, and a request without a code challenge is rejected. TeamsMaestro issues each integration its own access token, granted by the account holder and revocable by them, so the account holder's Microsoft credentials are never exposed to the integration.
Authorization: Bearer <access token>
Scopes are not used, so omit the scope parameter. Any explicit scope value is rejected with invalid_scope. A token grants whatever its account holder is permitted to do, and each endpoint enforces that through the account's plan and organization settings.
Access tokens last one hour and refresh tokens ninety days, with rotation enabled, so store the refresh token returned by every refresh response. A grace period of 120 seconds lets concurrent refreshes both succeed. Client credentials are issued on request; there is no self-service registration, and client secrets are shown once, at registration.
A partner token reaches /integrations/partners/ and nothing else. Any other path returns 403. Treat that as out of scope rather than as an expired token: refreshing does not change the outcome, and because refresh tokens rotate, a refresh-and-retry loop burns through them.
GET /integrations/partners/me/
The identity of the account the access token belongs to. Use it as a connection test and as the source of a connection label. This is the only partner endpoint with no plan requirement, so it still answers for an account whose subscription has lapsed.
{
"email": "owner@example.com",
"name": "Alex Rivera",
"organization": "Example Ltd",
"partner": "Zapier",
"can_create_webhooks": true
}
email is stable and suitable as a key. name falls back to the email address when no name is set. organization is null for an individual account. partner names the platform the token was issued to. can_create_webhooks reflects the plan and organization settings at the time of the call, and both can change afterwards, so treat the status code of the subscribe request as the authoritative answer rather than gating on this value alone.
401 means the token is expired, revoked, or not a Bearer credential. 403 means its OAuth client is not a registered partner.
GET /integrations/partners/event-types/
The catalogue of meeting events a subscription can listen to. No plan requirement, so it can be read before entitlement is established. The response is a bare array and is not paginated.
[
{ "id": "meeting.transcribed", "label": "Meeting Transcribed" },
{ "id": "meeting.summarized", "label": "Meeting Summarized" }
]
Read this endpoint rather than hard-coding the list, since new event types appear here first. The two events fire independently and in that order, and each is evaluated separately, so a meeting with too little speech to summarize fires meeting.transcribed only.
POST /integrations/partners/subscriptions/
A subscription is a target URL plus the event types delivered to it. Requires an active plan with webhooks.
The body takes target_url (required, at most 2048 characters, must resolve to a public address), event_types (required, at least one of meeting.transcribed and meeting.summarized), filter_match (optional, all or any, default all) and filters (optional array, default empty).
{
"target_url": "https://hooks.example.com/teamsmaestro",
"event_types": ["meeting.summarized"],
"filter_match": "all",
"filters": [
{ "field": "meeting.participants", "operator": "contains", "value": ["@external"] }
]
}
It returns 201 Created.
{
"id": "0022c696-b12d-40e8-9c65-ba6f50d9bc38",
"name": "Alex Rivera's Zapier: 1",
"source": "zapier",
"target_url": "https://hooks.example.com/teamsmaestro",
"event_types": ["meeting.summarized"],
"status": "active",
"filter_match": "all",
"filters": [
{ "field": "meeting.participants", "operator": "contains", "value": ["@external"] }
],
"payload_version": 20260806,
"created_at": "2026-08-04T19:12:44.104382Z"
}
id identifies the subscription for deletion. name is generated and not settable. source is the partner platform that created it. status is one of active, inactive, disabled and expired, and only active delivers. payload_version is the payload contract and is immutable. No signing secret is returned; see the note at the end.
Repeating the call with the same target_url rewrites the existing subscription, event types and filters included, reactivates it, and still returns 201. A target URL not used before, or an existing subscription that is disabled or expired, creates a new one instead.
400 covers an invalid target URL, empty event types, or a rejected filter. 403 covers a plan or organization restriction.
curl -X POST https://teams-app.maestrolabs.com/integrations/partners/subscriptions/ \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"target_url":"https://hooks.example.com/teamsmaestro","event_types":["meeting.summarized"]}'
Per-event subscribe endpoints
The same operation with the event type fixed by the path, for a platform that needs one operation per trigger.
POST /integrations/partners/subscriptions/meeting-transcribed/
POST /integrations/partners/subscriptions/meeting-summarized/
The body is identical minus event_types, which the path overrides, and so is the response, plus a Location header.
GET /integrations/partners/subscriptions/
Lists the subscriptions this account created through the calling partner. Subscriptions created in the TeamsMaestro applications, or through a different partner, are not returned. The response is a bare array and is not paginated.
GET /integrations/partners/subscriptions/{id}/ returns one subscription in the same shape as the create response, and 404 if it belongs to another account or another partner.
DELETE /integrations/partners/subscriptions/{id}/
Returns 204 No Content. This needs only a valid partner token, so an account with a lapsed plan can still unsubscribe.
POST /integrations/partners/subscriptions/{id}/send-sample/
Delivers a sample payload to the subscription's own target URL, signed with its own secret, once per event type on the subscription. Nothing is persisted, retried, or filtered. The body is empty.
{
"results": [
{
"event": "meeting.summarized",
"delivery_id": "evt_54f1c640-6c05-4147-9568-14b91bf67689",
"payload": { "...": "the exact body that was POSTed" },
"result": "succeeded",
"response_status_code": 200,
"response_body": "ok",
"error_message": ""
}
]
}
delivery_id is the X-Webhook-ID the delivery carried. result is succeeded, failed or cancelled. response_status_code and response_body come from the receiving endpoint and are null when no response arrived, in which case error_message is populated. A failure at the receiving endpoint is reported as a result inside a 200 response.
GET /integrations/partners/events/
Past meetings rendered in the same shape as a webhook delivery, so an integration can be built and tested before a live meeting has happened. Neither this endpoint nor the sample one below delivers events. Requires an active plan with webhooks.
The query takes event_type (required, meeting.transcribed or meeting.summarized) and limit (optional, 1 to 25, default 3). The response is a bare array of delivery envelopes, newest first.
Three things differ from a live delivery: id is generated per call and is not an idempotency key, webhook_id refers to a placeholder subscription, and filters are not applied. An account with no meetings of that type gets an empty array, in which case fall back to the sample endpoint.
GET /integrations/partners/events/sample/
Synthetic payloads, for an account with no meetings. The optional event_type parameter restricts the response to one type, and omitting it returns all. Three samples come back per event type, differing in host, meeting platform, whether the transcript carries per-utterance segments, and which template produced the summary. Implement against all three.
Signature verification
Partner subscriptions do not return a signing secret, and no partner endpoint exposes one. Deliveries are signed, but the key cannot be obtained through this surface. Where signature verification is required, the webhook has to be created in the TeamsMaestro web application instead. Otherwise treat the target URL as the credential: generate it per subscription, make it unguessable, and do not reuse it.