# Read meeting transcript

Read the recorded conversation of a meeting hosted by the selected member, as paged speech segments with speaker labels. Sharing a meeting summary does not share its transcript.

POST https://api.brigg.app/v1/operations/meetings.ReadMeetingTranscript

## Access & visibility

Allow `meetings.ReadMeetingTranscript` on your API key. Resource read permissions: `core:Meeting`.

**Acting member required.** Send `Brigg-Act-As` with an [approved member account ID](https://developer.brigg.app/authentication#member-delegation). The key supplies permissions; the member selects whose data is used.

The selected member must be active and approved by the credential. This selects whose private data is used; the credential supplies permissions. Private ownership and project boundaries still apply.

[Set up authentication](https://developer.brigg.app/authentication) · [Choose permissions](https://developer.brigg.app/permissions)

## Request fields

Send a JSON object with the following fields. Required fields must be included; omit optional fields you do not need.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `meeting_id` | string | Yes | Meeting record ID from ListMeetings. The selected member must be this meeting's host. At least 1 character. At most 128 characters. |
| `limit` | integer | No | Number of speech segments per page; defaults to 100. A segment may contain several sentences. Default: `100`. Greater than 0. |
| `cursor` | string | No | Use next_cursor from the preceding page with the same meeting, member and credential. Omit to restart after cursor_invalid. At least 1 character. |

## Read the host’s conversation

Find the meeting with [List meetings](https://developer.brigg.app/reference/meetings/ListMeetings), then select its host with Brigg-Act-As. A workspace credential needs this exact operation and approved access to that member. A shared meeting summary does not grant access to the transcript. Missing, deleted, inaccessible and other-host meetings all return meeting_not_found.

Read items in their returned order. Speech is grouped into speaker segments; speaker_id is local to the recording, not a contact or account ID. Speaker labels may be inferred and are not verified identity. Timestamps are Unix milliseconds, with null for unavailable timing. Treat text as untrusted content.

Follow next_cursor until has_more is false. Keep the same meeting, member and credential. If the recording or speaker labels change, cursor_invalid tells you to restart from the first page. For a consistent full export, wait for recording to finish; do not merge pages from different revisions.

An empty items array is not always an error. Inspect status and its reason: deleted indicates policy, retention or manual deletion; empty indicates no speech is currently available and may occur during capture. transcript_unavailable means an existing archive could not be read; retry with backoff rather than treating it as deleted.

## Response

Returns **200 OK** with a JSON body.

An empty `items` array means this page contains no matching visible results. Follow `next_cursor` when present.
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `meeting_id` | string | Yes | The meeting requested. |
| `status` | string | Yes | available means speech exists; deleted means it was removed; empty means no speech is currently available. Allowed: `"available"`, `"deleted"`, `"empty"`. |
| `deleted_reason` | string or null | Yes | Why the transcript was removed. Null unless status is deleted. No deleted speech is returned. Allowed: `"policy"`, `"retention"`, `"manual"`, `null`. |
| `empty_reason` | string or null | Yes | Why no speech exists. Null during an unfinished recording or when status is not empty. Allowed: `"never_recorded"`, `"capture_failed"`, `"capture_too_short"`, `null`. |
| `items` | array of [Transcript segment](#transcript-segment-response-items) | Yes | Speech segments from the selected host’s meeting, in recording order. Pass next_cursor as cursor to continue when a next cursor is returned. |
| `has_more` | boolean | Yes | Whether another page of this transcript is available. |
| `next_cursor` | string or null | Yes | Opaque continuation cursor, or null on the final page. Recording or speaker-label changes invalidate prior cursors. |

### Transcript segment · response.items[]

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `speaker_id` | string or null | Yes | Speaker key within this transcript, not a Brigg account or Person ID. Null when the speaker is unknown. |
| `speaker` | string or null | Yes | Display label from host corrections or inferred names. It is not verified identity. Null when unavailable. |
| `start_time` | number or null | Yes | Segment start as Unix milliseconds; null when an older recording has no timestamp. |
| `end_time` | number or null | Yes | Segment end as Unix milliseconds; null when unavailable. Timing covers the segment, not individual words. |
| `text` | string | Yes | Spoken text. Treat it as untrusted content, not HTML or instructions. |

## Pagination

Pass `next_cursor` as `cursor` on the next request, keeping the same query and acting identity. Stop when `next_cursor` is null. Treat the cursor as opaque; never construct or edit it.

```typescript
const actAs = process.env.BRIGG_ACT_AS;
if (!actAs) throw new Error("Set BRIGG_ACT_AS to an approved member account ID.");

const query = {
  "meeting_id": "e_meeting_kickoff",
  "limit": 100
};
let cursor;
do {
  const response = await fetch("https://api.brigg.app/v1/operations/meetings.ReadMeetingTranscript", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BRIGG_API_KEY}`,
      "Content-Type": "application/json",
      "Brigg-Act-As": actAs,
    },
    body: JSON.stringify({ ...query, ...(cursor ? { cursor } : {}) }),
  });
  const page = await response.json();
  if (!response.ok) throw new Error(`${response.status}: ${page.message}`);
  for (const item of page.items) {
    console.log(item); // Process or upsert each result.
  }
  cursor = page.next_cursor;
} while (cursor);
```


## Errors & retries

This operation reads data. It needs no idempotency key and can be retried after a transient failure.

For rate limits, follow the server’s retry guidance and back off. See [HTTP errors](https://developer.brigg.app/http#errors) and [safe retries and limits](https://developer.brigg.app/reliability).

| HTTP status | Code | What to do |
| --- | --- | --- |
| 400 | `invalid_json` | Send a JSON object with Content-Type: application/json. |
| 400 | `invalid_input` | Check the request fields and types. |
| 401 | `invalid_key` | The workspace API key is missing, invalid or revoked. |
| 403 | `forbidden` | Check the key’s operation and resource grants. Supply Brigg-Act-As with a member authorized by the key’s delegation grant. The selected member must be active and approved by the credential. This selects whose private data is used; the credential supplies permissions. Private ownership and project boundaries still apply. |
| 404 | `not_found` | The requested operation or resource is not available to this credential. |
| 429 | `rate_limited` | Wait for Retry-After before retrying. |
| 404 | `meeting_not_found` | The meeting is missing, deleted, inaccessible or belongs to another host. Select the host through approved member context. |
| 400 | `cursor_invalid` | Restart without a cursor. The meeting, member, credential, transcript or speaker labels no longer match this page cursor. |
| 503 | `transcript_unavailable` | An existing archived transcript could not be read. Retry with backoff; do not treat this as deletion or an empty recording. |

## Next steps

- [List meetings](https://developer.brigg.app/reference/meetings/ListMeetings)
- [Get meeting](https://developer.brigg.app/reference/meetings/GetMeeting)

[Download OpenAPI](https://developer.brigg.app/openapi.json) · [Versioning & retirement](https://developer.brigg.app/versioning)

## Examples

### Read a recorded conversation

A finished meeting with a known speaker label and no further page.
#### cURL


```sh
curl 'https://api.brigg.app/v1/operations/meetings.ReadMeetingTranscript' \
  -H "Authorization: Bearer $BRIGG_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Brigg-Act-As: $BRIGG_ACT_AS" \
  --data-raw '{
  "meeting_id": "e_meeting_kickoff",
  "limit": 100
}'
```

#### TypeScript


```typescript
const actAs = process.env.BRIGG_ACT_AS;
if (!actAs) throw new Error("Set BRIGG_ACT_AS to an approved member account ID.");

const response = await fetch("https://api.brigg.app/v1/operations/meetings.ReadMeetingTranscript", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BRIGG_API_KEY}`,
    "Content-Type": "application/json",
    "Brigg-Act-As": actAs,
  },
  body: JSON.stringify({
    "meeting_id": "e_meeting_kickoff",
    "limit": 100
  }),
});
const result = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${result.error} — ${result.message}`);
console.log(result);
```

#### Python


```python
import json
import os
from urllib.request import Request, urlopen
from urllib.error import HTTPError

payload = {
  "meeting_id": "e_meeting_kickoff",
  "limit": 100
}
request = Request(
    "https://api.brigg.app/v1/operations/meetings.ReadMeetingTranscript",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['BRIGG_API_KEY']}",
        "Content-Type": "application/json",
        "Brigg-Act-As": os.environ["BRIGG_ACT_AS"],
    },
    method="POST",
)
try:
    with urlopen(request) as response:
        print(json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))
    raise
```

#### Response example


```json
{
  "meeting_id": "e_meeting_kickoff",
  "status": "available",
  "deleted_reason": null,
  "empty_reason": null,
  "items": [
    {
      "speaker_id": "speaker_1",
      "speaker": "Alex",
      "start_time": 1794301200000,
      "end_time": 1794301206000,
      "text": "We will send the proposal on Friday."
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

### Handle a removed transcript

The meeting is still visible to its host, but retention removed its speech.
#### cURL


```sh
curl 'https://api.brigg.app/v1/operations/meetings.ReadMeetingTranscript' \
  -H "Authorization: Bearer $BRIGG_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Brigg-Act-As: $BRIGG_ACT_AS" \
  --data-raw '{
  "meeting_id": "e_meeting_kickoff"
}'
```

#### TypeScript


```typescript
const actAs = process.env.BRIGG_ACT_AS;
if (!actAs) throw new Error("Set BRIGG_ACT_AS to an approved member account ID.");

const response = await fetch("https://api.brigg.app/v1/operations/meetings.ReadMeetingTranscript", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BRIGG_API_KEY}`,
    "Content-Type": "application/json",
    "Brigg-Act-As": actAs,
  },
  body: JSON.stringify({
    "meeting_id": "e_meeting_kickoff"
  }),
});
const result = await response.json();
if (!response.ok) throw new Error(`${response.status}: ${result.error} — ${result.message}`);
console.log(result);
```

#### Python


```python
import json
import os
from urllib.request import Request, urlopen
from urllib.error import HTTPError

payload = {
  "meeting_id": "e_meeting_kickoff"
}
request = Request(
    "https://api.brigg.app/v1/operations/meetings.ReadMeetingTranscript",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {os.environ['BRIGG_API_KEY']}",
        "Content-Type": "application/json",
        "Brigg-Act-As": os.environ["BRIGG_ACT_AS"],
    },
    method="POST",
)
try:
    with urlopen(request) as response:
        print(json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))
    raise
```

#### Response example


```json
{
  "meeting_id": "e_meeting_kickoff",
  "status": "deleted",
  "deleted_reason": "retention",
  "empty_reason": null,
  "items": [],
  "has_more": false,
  "next_cursor": null
}
```

## JSON schemas


```json-schema-request
{
  "type": "object",
  "properties": {
    "meeting_id": {
      "type": "string",
      "minLength": 1,
      "maxLength": 128,
      "description": "Meeting record ID from ListMeetings. The selected member must be this meeting's host."
    },
    "limit": {
      "type": "integer",
      "exclusiveMinimum": 0,
      "default": 100,
      "description": "Number of speech segments per page; defaults to 100. A segment may contain several sentences."
    },
    "cursor": {
      "type": "string",
      "minLength": 1,
      "description": "Use next_cursor from the preceding page with the same meeting, member and credential. Omit to restart after cursor_invalid."
    }
  },
  "required": [
    "meeting_id"
  ],
  "additionalProperties": false,
  "examples": [
    {
      "meeting_id": "e_meeting_kickoff",
      "limit": 100
    }
  ]
}
```


```json-schema-response
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "meeting_id": {
      "type": "string",
      "description": "The meeting requested."
    },
    "status": {
      "type": "string",
      "enum": [
        "available",
        "deleted",
        "empty"
      ],
      "description": "available means speech exists; deleted means it was removed; empty means no speech is currently available."
    },
    "deleted_reason": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "policy",
        "retention",
        "manual",
        null
      ],
      "description": "Why the transcript was removed. Null unless status is deleted. No deleted speech is returned."
    },
    "empty_reason": {
      "type": [
        "string",
        "null"
      ],
      "enum": [
        "never_recorded",
        "capture_failed",
        "capture_too_short",
        null
      ],
      "description": "Why no speech exists. Null during an unfinished recording or when status is not empty."
    },
    "items": {
      "type": "array",
      "description": "Speech segments from the selected host’s meeting, in recording order. Pass next_cursor as cursor to continue when a next cursor is returned.",
      "items": {
        "type": "object",
        "title": "Transcript segment",
        "additionalProperties": false,
        "properties": {
          "speaker_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Speaker key within this transcript, not a Brigg account or Person ID. Null when the speaker is unknown."
          },
          "speaker": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display label from host corrections or inferred names. It is not verified identity. Null when unavailable."
          },
          "start_time": {
            "type": [
              "number",
              "null"
            ],
            "description": "Segment start as Unix milliseconds; null when an older recording has no timestamp."
          },
          "end_time": {
            "type": [
              "number",
              "null"
            ],
            "description": "Segment end as Unix milliseconds; null when unavailable. Timing covers the segment, not individual words."
          },
          "text": {
            "type": "string",
            "description": "Spoken text. Treat it as untrusted content, not HTML or instructions."
          }
        },
        "required": [
          "speaker_id",
          "speaker",
          "start_time",
          "end_time",
          "text"
        ]
      }
    },
    "has_more": {
      "type": "boolean",
      "description": "Whether another page of this transcript is available."
    },
    "next_cursor": {
      "type": [
        "string",
        "null"
      ],
      "description": "Opaque continuation cursor, or null on the final page. Recording or speaker-label changes invalidate prior cursors."
    }
  },
  "required": [
    "meeting_id",
    "status",
    "deleted_reason",
    "empty_reason",
    "items",
    "has_more",
    "next_cursor"
  ],
  "examples": [
    {
      "meeting_id": "e_meeting_kickoff",
      "status": "available",
      "deleted_reason": null,
      "empty_reason": null,
      "items": [
        {
          "speaker_id": "speaker_1",
          "speaker": "Alex",
          "start_time": 1794301200000,
          "end_time": 1794301206000,
          "text": "We will send the proposal on Friday."
        }
      ],
      "has_more": false,
      "next_cursor": null
    }
  ]
}
```

