Endpoints
API Domain is: https://api.grain.com
OAuth2
Generate Token
Endpoint
POST /_/public-api/oauth2/tokenParams
| Param | |
|---|---|
| client_id | Obtained separately in Step 1 of the OAuth Flow |
| client_secret | Obtained separately in Step 1 of the OAuth Flow * Required for server-side applications |
| grant_type | Must be authorization_code |
| code | String obtained in Step 4 of the OAuth Flow |
| code_verifier | String sent as code_challenge in Step 2 of the OAuth Flow |
Response
| Field | |
|---|---|
| token_type | Always bearer |
| access_token | Token to be used in the Authorization header |
| refresh_token | Token to be used to refresh the access_token. (See Refresh Token) |
| expires_in | Denotes how many seconds the access_token will live for after its generation. |
NOTE: Some legacy clients do not return
refresh_tokennorexpires_in.
For these cases the access_token does not expire.
Example Request
curl -X POST \
-H 'Content-Type: application/json' \
--data '{"grant_type": "authorization_code", "code": "CODE", "client_id": "CLIENT_ID", "client_secret": "CLIENT_SECRET"}' \
https://api.grain.com/_/public-api/oauth2/tokenExample Response
{
"token_type": "bearer",
"access_token": "aaaaa33333EEEEENNNNNoooooKKKKK33333lllll",
"refresh_token": "IIIIIQQQQQxxxxx4444422222CCCCCoooooWWWWW",
"expires_in": 3600
}Refresh Token
Endpoint
POST /_/public-api/oauth2/tokenParams
| Param | |
|---|---|
| client_id | Obtained separately in Step 1 of the OAuth Flow |
| client_secret | Obtained separately in Step 1 of the OAuth Flow * Required for server-side applications |
| grant_type | Must be refresh_token |
| refresh_token | Obtained when generating or refreshing an access_token |
Response
| Field | |
|---|---|
| token_type | Always bearer |
| access_token | Token to be used in the Authorization header |
| refresh_token | Token to be used to re-refresh the access_token |
| expires_in | Denotes how many seconds the access_token will live for after its generation. |
Example Request
curl -X POST \
-H 'Content-Type: application/json' \
--data '{"grant_type": "refresh_token", "refresh_token": "IIIIIQQQQQxxxxx4444422222CCCCCoooooWWWWW", "client_id": "CLIENT_ID", "client_secret": "CLIENT_SECRET"}' \
https://api.grain.com/_/public-api/oauth2/tokenExample Response
{
"token_type": "bearer",
"access_token": "bbbbb44444FFFFFOOOOOpppppLLLLL44444mmmmm",
"refresh_token": "JJJJJRRRRRyyyyy5555533333DDDDDpppppXXXXX",
"expires_in": 3600
}Recordings
List Recordings
Endpoint
POST /_/public-api/v2/recordingsParams
| Param | Type | |
|---|---|---|
| cursor | string | Used to paginate through the list. |
| filter | object | Recording Filter |
| include | object | Recording Include |
Response
| Field | Type | |
|---|---|---|
| cursor | string *nullable |
Can be sent as a param to retrieve the next page of the list |
| recordings | object array | List of Recordings |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"filter": {"title_search": "hands"}, "include": {"participants": true}}' \
https://api.grain.com/_/public-api/v2/recordingsExample Response
{
"cursor": "ApJNWoNoBHcCdjJ3DWNocm9ub2xvZ2ljYWx3BGRlc2NoAm4HAO5FaSm2QgZuBwAVPC0qtkIG",
"recordings": [
{
"id": "pppp6666-qq77-rr88-ss99-tttt00000000",
"title": "All Hands",
"source": "zoom",
"share_state": "public",
"url": "https://grain.com/share/recording/pppp6666-qq77-rr88-ss99-tttt00000000",
"media_type": "video",
"tags": [],
"start_datetime": "2026-01-01T09:30:00Z",
"teams": [
{
"id": "aaaa1111-bb22-cc33-dd44-eeee55555555",
"name": "My Team"
}
],
"workspace_shared": false,
"recorders": [
{
"id": "kkkk1111-ll22-mm33-nn44-oooo55555555",
"name": "Luke Skywalker",
"email": "luke@example.com",
"participant_id": "eeee1111-ff22-gg33-hh44-iiii55555555"
}
],
"participants": [
{
"id": "eeee1111-ff22-gg33-hh44-iiii55555555",
"name": "Luke Skywalker",
"scope": "internal",
"email": "luke@example.com",
"confirmed_attendee": true
},
{
"id": "jjjj6666-kk77-ll88-mm99-nnnn00000000",
"name": "Han Solo",
"scope": "internal",
"email": "solo@example.com",
"confirmed_attendee": false
}
],
"end_datetime": "2026-01-01T10:00:00Z",
"duration_ms": 1800000,
"thumbnail_url": "https://media.grain.com/public_thumbnails/recordings/pppp6666",
"meeting_type": {
"id": "ffff6666-gg77-hh88-ii99-jjjj00000000",
"name": "Project & Team Coordination",
"scope": "internal"
}
}
]
}Get Recording
Endpoint
POST /_/public-api/v2/recordings/:recording_idParams
| Param | Type | |
|---|---|---|
| include | object | Recording Include |
Response
| Field | Type |
|---|---|
| Recording | object |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"include": {"participants": true, "calendar_event": true}}' \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000Example Response
{
"id": "pppp6666-qq77-rr88-ss99-tttt00000000",
"title": "All Hands",
"source": "zoom",
"share_state": "public",
"url": "https://grain.com/share/recording/pppp6666-qq77-rr88-ss99-tttt00000000",
"media_type": "video",
"tags": [],
"start_datetime": "2026-01-01T09:30:00Z",
"teams": [
{
"id": "aaaa1111-bb22-cc33-dd44-eeee55555555",
"name": "My Team"
}
],
"workspace_shared": false,
"recorders": [
{
"id": "kkkk1111-ll22-mm33-nn44-oooo55555555",
"name": "Luke Skywalker",
"email": "luke@example.com",
"participant_id": "eeee1111-ff22-gg33-hh44-iiii55555555"
}
],
"participants": [
{
"id": "eeee1111-ff22-gg33-hh44-iiii55555555",
"name": "Luke Skywalker",
"scope": "internal",
"email": "luke@example.com",
"confirmed_attendee": true
},
{
"id": "jjjj6666-kk77-ll88-mm99-nnnn00000000",
"name": "Han Solo",
"scope": "internal",
"email": "solo@example.com",
"confirmed_attendee": false
}
],
"end_datetime": "2026-01-01T10:00:00Z",
"duration_ms": 1800000,
"thumbnail_url": "https://media.grain.com/public_thumbnails/recordings/pppp6666",
"meeting_type": {
"id": "ffff6666-gg77-hh88-ii99-jjjj00000000",
"name": "Project & Team Coordination",
"scope": "internal"
},
"calendar_event": {
"ical_uid": "meeting-abc123",
"scheduled_start_datetime": "2026-01-01T09:00:00Z",
"scheduled_end_datetime": "2026-01-01T10:00:00Z"
}
}Get Recording Transcript (json)
Endpoint
GET /_/public-api/v2/recordings/:recording_id/transcriptParams
N/A
Response
| Field | Type | |
|---|---|---|
| participant_id | UUID string *nullable |
id of the participant |
| speaker | string | Name of the participant |
| start | integer | Timestamp in ms from the recording of when this section started. |
| end | integer | Timestamp in ms from the recording of when this section ended. |
| text | string | Text of this transcript section. |
Example Request
curl \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/transcriptExample Response
[
{
"start": 8000,
"text": "Hello there.",
"end": 9000,
"speaker": "Obi Wan Kenobi",
"participant_id": "oooo1111-pp22-qq33-rr44-ssss55555555"
},
{
"start": 11482,
"text": "General Kenobi...",
"end": 13000,
"speaker": "General Grievous",
"participant_id": "yyyy1111-zz22-aabb-cc44-dddd55555555"
}
]Get Recording Transcript (text formats)
Endpoint
GET /_/public-api/v2/recordings/:recording_id/transcript.txtGET /_/public-api/v2/recordings/:recording_id/transcript.vttGET /_/public-api/v2/recordings/:recording_id/transcript.srtParams
N/A
Response
Transcript text
Example Request
curl \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/transcript.txtExample Response
Obi Wan Kenobi: Hello there.
General Grievous: General Kenobi...Download Recording
Endpoint
GET /_/public-api/v2/recordings/:recording_id/downloadParams
N/A
Response
Recording file
Example Request
curl \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
-L --output recording.mp4 \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/downloadExample Response
Recording file
Update Recording
Endpoint
PATCH /_/public-api/v2/recordings/:recording_idParams
| Param | Type | |
|---|---|---|
| title | string | New title for the recording |
Response
| Field | Type |
|---|---|
| success | Always true |
Example Request
curl -X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"title": "A new title"}' \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000Example Response
{
"success": true
}Add a Tag to a Recording
Endpoint
PUT /_/public-api/v2/recordings/:recording_id/tagsParams
| Param | Type | |
|---|---|---|
| tag | Regex checked string format: (Regex101) /^[\p{L}\d][\p{L}\d-]*$/u |
Tag to be added to the recording (Letters and numbers separated by dashes - ) |
Response
| Field | Type |
|---|---|
| success | Always true |
Example Request
curl -X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"tag": "my-new-tag"}' \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/tagsExample Response
{
"success": true
}Remove a Tag from a Recording
Endpoint
DELETE /_/public-api/v2/recordings/:recording_id/tags/:tagParams
N/A
Response
| Field | Type |
|---|---|
| success | Always true |
Example Request
curl -X DELETE \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/tags/my-new-tagExample Response
{
"success": true
}Share Recording to a User
Endpoint
PUT /_/public-api/v2/recordings/:recording_id/usersParams
| Param | Type | |
|---|---|---|
| user_id | UUID string | User to share the recording with. Obtained through List Users |
Response
| Field | Type |
|---|---|
| success | Always true |
Example Request
curl -X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"user_id": "7890-abcd-ef01-2222-3456-7890ffff"}' \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/usersExample Response
{
"success": true
}Unshare Recording from a User
Endpoint
DELETE /_/public-api/v2/recordings/:recording_id/users/:user_idParams
N/A
Response
| Field | Type |
|---|---|
| success | Always true |
Example Request
curl -X DELETE \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/users/7890-abcd-ef01-2222-3456-7890ffffExample Response
{
"success": true
}Share Recording to a Team
Endpoint
PUT /_/public-api/v2/recordings/:recording_id/teamsParams
| Param | Type | |
|---|---|---|
| team_id | UUID string | Team to share the recording with. Obtained through List Teams |
Response
| Field | Type |
|---|---|
| success | Always true |
Example Request
curl -X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"team_id": "aaaa1111-bb22-cc33-dd44-eeee55555555"}' \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/teamsExample Response
{
"success": true
}Unshare Recording from a Team
Endpoint
DELETE /_/public-api/v2/recordings/:recording_id/teams/:team_idParams
N/A
Response
| Field | Type |
|---|---|
| success | Always true |
Example Request
curl -X DELETE \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/recordings/pppp6666-qq77-rr88-ss99-tttt00000000/teams/aaaa1111-bb22-cc33-dd44-eeee55555555Example Response
{
"success": true
}Uploads
Create Upload
Creates an upload for the given media_type, which becomes a recording.
See the Uploads page for details and examples.
Endpoint
POST /_/public-api/v2/uploads/createParams
| Param | Type | |
|---|---|---|
| media_type required |
One of: - video- audio- transcript |
Media type of the upload |
| auto_promote | boolean | Whether the upload continues to promoting or stays in staged.Defaults to true |
| include | object | Upload Include |
| attributes required for transcript |
object | Upload Attributes Input |
Response
| Field | Type | |
|---|---|---|
| Upload | object |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"media_type": "video", "auto_promote": false}' \
https://api.grain.com/_/public-api/v2/uploads/createExample Response
{
"id": "eeee1111-ff22-gg33-hh44-iiii55555555",
"media_type": "video",
"title": "Recording Upload 2026-01-01 09:30",
"status": "awaiting",
"auto_promote": false,
"recording_id": null,
"created_datetime": "2026-01-01T09:30:00Z",
"updated_datetime": "2026-01-01T09:30:00Z",
"media": {
"status": "awaiting",
"upload": {
"url": "https://example.com/generated_url",
"expires_at": "2026-01-02T09:30:00Z",
"headers": {
"If-None-Match": "*"
},
"max_duration_sec": 10800,
"max_upload_bytes": 4294967296
}
}
}List Uploads
Endpoint
POST /_/public-api/v2/uploadsParams
| Param | Type | |
|---|---|---|
| cursor | string | Cursor obtained from a previous List Uploads response, for pagination |
| include | object | Upload Include |
| filter | object | Upload Filter |
Response
| Field | Type | |
|---|---|---|
| uploads | object array | List of Uploads |
| cursor | string *nullable |
Pass to the next List Uploads call to get the next page |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"filter": {"media_type": "video"}}' \
https://api.grain.com/_/public-api/v2/uploadsExample Response
{
"uploads": [
{
"id": "eeee1111-ff22-gg33-hh44-iiii55555555",
"media_type": "video",
"title": "All Hands (corrected)",
"status": "success",
"auto_promote": false,
"recording_id": "pppp6666-qq77-rr88-ss99-tttt00000000",
"created_datetime": "2026-01-01T09:30:00Z",
"updated_datetime": "2026-01-01T09:42:00Z"
}
],
"cursor": null
}Get Upload
Endpoint
POST /_/public-api/v2/uploads/:upload_idParams
| Param | Type | |
|---|---|---|
| include | object | Upload Include |
Response
| Field | Type | |
|---|---|---|
| Upload | object |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/uploads/eeee1111-ff22-gg33-hh44-iiii55555555Example Response
{
"id": "eeee1111-ff22-gg33-hh44-iiii55555555",
"media_type": "video",
"title": "Recording Upload 2026-01-01 09:30",
"status": "staged",
"auto_promote": false,
"recording_id": null,
"created_datetime": "2026-01-01T09:30:00Z",
"updated_datetime": "2026-01-01T09:35:00Z",
"media": {
"status": "uploaded",
"expires_at": "2026-01-08T09:35:00Z"
}
}Update Upload
Updates the attributes of an upload whose status is awaiting or staged with auto_promote: false.
Endpoint
PATCH /_/public-api/v2/uploads/:upload_idParams
| Param | Type | |
|---|---|---|
| include | object | Upload Include |
| attributes | object | Upload Attributes Input |
Response
| Field | Type | |
|---|---|---|
| Upload | object |
Example Request
curl -X PATCH \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"attributes": {"title": "All Hands (corrected)"}}' \
https://api.grain.com/_/public-api/v2/uploads/eeee1111-ff22-gg33-hh44-iiii55555555Example Response
{
"id": "eeee1111-ff22-gg33-hh44-iiii55555555",
"media_type": "video",
"title": "All Hands (corrected)",
"status": "staged",
"auto_promote": false,
"recording_id": null,
"created_datetime": "2026-01-01T09:30:00Z",
"updated_datetime": "2026-01-01T09:40:00Z"
}Promote Upload
Manually starts promoting a staged upload that was created with auto_promote: false.
See the Uploads page.
Endpoint
POST /_/public-api/v2/uploads/:upload_id/promoteParams
| Param | Type | |
|---|---|---|
| include | object | Upload Include |
Response
| Field | Type | |
|---|---|---|
| Upload | object |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/uploads/eeee1111-ff22-gg33-hh44-iiii55555555/promoteExample Response
{
"id": "eeee1111-ff22-gg33-hh44-iiii55555555",
"media_type": "video",
"title": "All Hands (corrected)",
"status": "promoting",
"auto_promote": false,
"recording_id": null,
"created_datetime": "2026-01-01T09:30:00Z",
"updated_datetime": "2026-01-01T09:41:00Z"
}Hooks
Create Hook
Creates a hook to be notified of a hook_type event.
See the Hooks page for hook types and the delivered payload shape.
Endpoint
POST /_/public-api/v2/hooks/createParams
| Param | Type | |
|---|---|---|
| hook_url required |
string Secure ( https://) URL |
Endpoint to be called when the event is triggered. |
| hook_type required |
One of Hook Types | Type of notifications the hook will be sent. |
| include | object | See Hook Types for include support per hook_type |
NOTE: A
POSTreachability test is made tohook_urlon creation.
It must respond with a2xxstatus, or the hook won't be created.
Response
| Field | Type |
|---|---|
| Hook | object |
Example Request
curl \
-X POST \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-H "Public-Api-Version: 2026-10-01" \
--data '{"hook_type": "recording_added", "hook_url": "https://example.com/hook"}' \
https://api.grain.com/_/public-api/v2/hooks/createExample Response
{
"enabled": true,
"id": "zzzz6666-aa77-bb88-cc99-dddd00000000",
"include": {},
"inserted_at": "2026-01-01T09:30:00Z",
"hook_url": "https://example.com/hook",
"hook_type": "recording_added"
}List Hooks
Endpoint
POST /_/public-api/v2/hooksParams
| Param | Type | |
|---|---|---|
| filter | object | Hook Filter |
Response
| Field | Type | |
|---|---|---|
| hooks | object array | List of Hooks |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/hooksExample Response
{
"hooks": [
{
"enabled": true,
"id": "zzzz6666-aa77-bb88-cc99-dddd00000000",
"include": {},
"inserted_at": "2026-01-01T09:30:00Z",
"hook_url": "https://example.com/hook",
"hook_type": "recording_added"
}
]
}Delete Hook
Endpoint
DELETE /_/public-api/v2/hooks/:hook_idParams
N/A
Response
| Field | Type |
|---|---|
| success | Always true |
Example Request
curl -X DELETE \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/hooks/zzzz6666-aa77-bb88-cc99-dddd00000000Example Response
{
"success": true
}Others
Token Info
Endpoint
GET /_/public-api/v2/token-infoParams
N/A
Response
| Field | Type | |
|---|---|---|
| type | One of: - oauth- personal_access_token- workspace_access_token |
Type of token used to authenticate. See Token Types |
| scope | One of: - personal- workspace |
Scope of the token. See Token Types |
| user_id scope: personal |
UUID string | Id of the token's owner |
| workspace_id scope: workspace |
UUID string | Id of the token's owning workspace |
| name | string | Name of the user or workspace |
| expires_at | ISO8601 formatted timestamp2026-01-01T09:30:00Z*nullable |
UTC datetime the token expires.null indicates the token doesn't expire |
Example Request
curl -X GET \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/token-infoExample Response
{
"type": "personal_access_token",
"scope": "personal",
"user_id": "7890-abcd-ef01-2222-3456-7890ffff",
"name": "Luke Skywalker",
"expires_at": null
}List Users
Endpoint
POST /_/public-api/v2/usersParams
N/A
Response
| Field | Type | |
|---|---|---|
| users | object array | List of Users |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/usersExample Response
{
"users": [
{
"id": "7890-abcd-ef01-2222-3456-7890ffff",
"name": "Luke Skywalker",
"email": "luke@example.com"
}
]
}List Teams
Endpoint
POST /_/public-api/v2/teamsParams
N/A
Response
| Field | Type | |
|---|---|---|
| teams | object array | List of Teams |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/teamsExample Response
{
"teams": [
{
"id": "aaaa1111-bb22-cc33-dd44-eeee55555555",
"name": "My Team"
}
]
}List Meeting Types
Endpoint
POST /_/public-api/v2/meeting_typesParams
N/A
Response
| Field | Type | |
|---|---|---|
| meeting_types | object array | List of Meeting Types |
Example Request
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
-H "Public-Api-Version: 2026-10-01" \
https://api.grain.com/_/public-api/v2/meeting_typesExample Response
{
"meeting_types": [
{
"id": "aaaa1111-bb22-cc33-dd44-eeee55555555",
"name": "Sales",
"scope": "external"
},
{
"id": "ffff6666-gg77-hh88-ii99-jjjj00000000",
"name": "Project & Team Coordination",
"scope": "internal"
}
]
}Common Request Objects
Recording Filter
This object is used to filter for specific recordings.
| Param | Type | |
|---|---|---|
| before_datetime | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
Only return recordings which start_datetime is after the selected date (exclusive) |
| after_datetime | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
Only return recordings which start_datetime is before the selected date (inclusive) |
| attendance only for personal scope |
One of: - hosted- attended |
hostedOnly return recordings where the user was the meeting host. attendedOnly return recordings where the user attended the meeting. |
| participant_scope | One of: - internal- external |
internalOnly return recordings of internal / team meetings. externalOnly return recordings of external / customer meetings. |
| title_search | string | Only return recordings which title matches the given search string. |
| team | uuid | Only return recordings which team matches the given team id. Team ids can be found through the List Teams endpoint. |
| meeting_type | uuid | Only return recordings which meeting type matches the given meeting type id. Meeting type ids can be found through the List Meeting Types endpoint. |
Recording Include
| Param | Type | |
|---|---|---|
| highlights | boolean | Include clips / highlights in the response |
| participants | boolean | Include participants in the response |
| ai_action_items | boolean | Include the ai_action_items in the response |
| ai_summary | boolean | Include the ai_summary in the response |
| private_notes only for personal scope |
boolean | Include your private notes in the response |
| calendar_event | boolean | Include calendar event data in the response |
| hubspot | boolean | Include HubSpot related data in the response |
| screenshares | boolean | Include screenshare ranges in the response |
| ai_template_sections | object | Includes ai_template_sections in the response |
| ai_template_sections format |
One of: - json- markdown- text |
Controls the output of ai_template_sections Defaults to json |
| ai_template_sections allowed_sections |
string array | Only include sections which title matches the given allowed_sectionsCase insensitive |
Highlight Include
| Param | Type | |
|---|---|---|
| transcript | boolean | Include the highlight's transcript in the response |
| speakers | boolean | Include the highlight's speakers in the response |
Hook Filter
| Param | Type | |
|---|---|---|
| hook_type | One of Hook Types | Only return hooks with the matching hook_type |
| state | One of: - enabled- disabled |
Only return hooks that are either enabled or disabled. |
Upload Attributes Input
All attributes are optional for an upload unless stated otherwise.
| Param | Type | |
|---|---|---|
| title | string | Title for the recording and upload |
| start_datetime | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
When the recording took place. Cannot be in the future. |
| team_id | uuid | Team to attach the resulting recording to. Team ids can be found through the List Teams endpoint. |
| recorder_ids required for workspace scope |
uuid array | Users to credit as recorders. User ids can be found through the List Users endpoint. |
| participants | object array | A participant must have at least one of name, email or phone. |
| participants name |
string | Name of the participant |
| participants |
string | Email of the participant |
| participants phone |
string | Phone number of the participant |
| participants speaker_key |
string | Used to match a participant to the speaker_key in transcript.segments |
| transcript required for transcript |
object | Transcript for the recording |
| transcript language |
string | Language code, e.g. en |
| transcript segments |
object array | |
| transcript segments speaker_key |
string | Used to match to a participants entry. An anonymous speaker will be made if none matches |
| transcript segments start |
integer | Segment start, in ms from the recording start |
| transcript segments end |
integer | Segment end, in ms from the recording start |
| transcript segments text |
string | Segment text |
Upload Include
| Param | Type | |
|---|---|---|
| attributes | boolean | Include the attributes in the response, save for the transcript |
| transcript | boolean | Include the transcript in the response |
Upload Filter
| Param | Type | |
|---|---|---|
| media_type | One of: - video- audio- transcript |
Only return uploads with the matching media_type |
Common Response Objects
Recording
| Field | Type | |
|---|---|---|
| id | UUID string | Id of the recording |
| title | string | Title of the recording |
| start_datetime | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
UTC datetime at which grain started the recording |
| end_datetime | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
UTC datetime at which the recording ended |
| duration_ms | integer | Duration of the recording in ms |
| media_type | One of: - video- audio- transcript |
Media type of the recording |
| source | One of: - aircall- local_capture- meet- teams- upload- webex- zoom- other |
From what source did Grain get the recording |
| share_state | One of: - public- workspace- restricted |
publicAnyone with the link workspaceAll workspace members restrictedOnly people with access |
| url | string | URL to the recording in Grain |
| thumbnail_url | string *nullable |
URL to the recording's thumbnail |
| highlights | object array | List of Highlights |
| tags | string array | Tags of the recording |
| teams | object array | List of Teams the recording belongs to. |
| workspace_shared | boolean | Whether the recording is shared with the whole workspace |
| recorders | object array | List of users that recorded the meeting |
| recorders id |
UUID string | User id of the recorder |
| recorders name |
string | Name of the recorder |
| recorders |
string | Email of the recorder |
| recorders participant_id include: participants |
UUID string *nullable |
Participant id of the recorder |
| meeting_type | object *nullable |
Recording's Meeting Type |
| ai_template_sections include: ai_template_sections |
object array | List of AI Template Sections |
| ai_action_items include: ai_action_items |
object array | List of Action Items |
| ai_action_items status |
One of: - pending- completed |
Status of the action item |
| ai_action_items timestamp |
integer | Observed timestamp in ms at which the action item was mentioned |
| ai_action_items text |
string | Content of the action item |
| ai_action_items assignee |
object *nullable |
Assignee of the action item |
| ai_action_items assignee id |
UUID string | Id of the assignee |
| ai_action_items assignee name |
string | Name of the assignee |
| ai_action_items assignee user_id |
UUID string *nullable |
User id of the assignee |
| ai_summary include: ai_summary |
object | |
| ai_summary text |
markdown formatted string | Markdown text of the ai summary |
| calendar_event include: calendar_event |
object *nullable |
|
| calendar_event ical_uid |
string *nullable |
Ical UID of the related event |
| calendar_event scheduled_start_datetime |
ISO8601 formatted timestamp2026-01-01T09:00:00Z*nullable |
UTC datetime of the scheduled calendar event start time |
| calendar_event scheduled_end_datetime |
ISO8601 formatted timestamp2026-01-01T10:00:00Z*nullable |
UTC datetime of the scheduled calendar event end time |
| hubspot include: hubspot |
object | |
| hubspot hubspot_company_ids |
string array | List of HubSpot company ids related to the recording |
| hubspot hubspot_deal_ids |
string array | List of HubSpot deal ids related to the recording |
| participants include: participants |
object array | |
| participants id |
UUID string | id of the participant |
| participants name |
string | Name of the participant |
| participants |
string *nullable |
Email of the participant |
| participants scope |
One of: - internal- external- unknown |
Scope of the participant |
| participants confirmed_attendee |
boolean | Whether or not the participant was present in the recording |
| participants hs_contact_id include: hubspot |
string *nullable |
HubSpot contact id of the participant |
| participants observed_join_time |
ISO8601 formatted timestamp2026-01-01T09:30:00Z*nullable |
UTC datetime the participant was observed joining the meeting. Note: This is based on what Grain's recorder observed and may not reflect the participant's actual join time. |
| participants observed_leave_time |
ISO8601 formatted timestamp2026-01-01T10:00:00Z*nullable |
UTC datetime the participant was observed leaving the meeting. Note: This is based on what Grain's recorder observed and may not reflect the participant's actual leave time. |
| private_notes include: private_notes |
object *nullable |
|
| private_notes text |
string | Markdown text of the user's private notes |
| screenshares include: screenshares |
object array | List of screenshare ranges |
| screenshares start |
integer | Timestamp in ms from the recording of when the screenshare range started |
| screenshares end |
integer | Timestamp in ms from the recording of when the screenshare range ended |
| screenshares participant_id |
UUID string | Id of the participant that was screensharing |
AI Template Sections
| Field | Type | |
|---|---|---|
| title | string | Title of the section |
| data | object | Fields depend on the type of section. |
Highlight (aka Clip)
| Field | Type | |
|---|---|---|
| id | UUID string | id of the highlight |
| recording_id | UUID string | id of the recording it was clipped from |
| text | string | Small description of the clip, equivalent to a title |
| transcript | string | Transcript of the clip formatted as text |
| speakers | string array | Names of the speakers of the clip |
| timestamp | integer | Timestamp in ms from the recording of when the clip starts |
| duration | integer | Duration of the clip in ms |
| tags | string array | Tags of the clip |
| url | string | URL to the highlight in Grain |
| thumbnail_url | string | URL to the highlight's thumbnail |
| created_datetime | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
UTC datetime of when the clip was created |
Upload
| Field | Type | |
|---|---|---|
| id | UUID string | Id of the upload |
| media_type | One of: - video- audio- transcript |
Media type of the upload |
| title | string | Title of the recording and upload |
| status | One of Upload Statuses | Status of the upload |
| auto_promote | boolean | Whether the upload stops in staged or continues to promoting |
| recording_id | UUID string *nullable |
Id of the resulting recording. Only set once status is success. |
| created_datetime | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
UTC datetime the upload was created |
| updated_datetime | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
UTC datetime the upload was last updated |
| attributes include: attributes include: transcript |
object | Upload Attributes |
| media | object *optional |
See Upload Media |
| failure status: failure |
object | Present only while status is failure |
| failure reason |
string | Human-readable description of what went wrong |
Upload Attributes
| Field | Type | |
|---|---|---|
| title | string | Title of the recording and upload |
| start_datetime include: attributes |
ISO8601 formatted timestamp2026-01-01T09:30:00Z*nullable |
When the recording took place |
| team_id include: attributes |
uuid *nullable |
Team the resulting recording is attached to |
| recorder_ids include: attributes |
uuid array | Users credited as recorders |
| participants include: attributes |
object array | |
| participants name |
string *nullable |
Name of the participant |
| participants |
string *nullable |
Email of the participant |
| participants phone |
string *nullable |
Phone number of the participant |
| participants speaker_key |
string *nullable |
Matches a transcript.segments entry to this participant |
| transcript include: transcript |
object | |
| transcript language |
string | Language code, e.g. en |
| transcript segments |
object array | |
| transcript segments speaker_key |
string | Matches a participants entry, or an Unmatched Speaker if none matches |
| transcript segments start |
integer | Segment start, in ms from the recording start |
| transcript segments end |
integer | Segment end, in ms from the recording start |
| transcript segments text |
string | Segment text |
| transcript anonymous_speakers |
object array | Auto-generated for segment speaker_keys with no matching participant. See Unmatched Speaker |
| transcript anonymous_speakers speaker_key |
string | The unmatched speaker_key this entry was generated for |
| transcript anonymous_speakers name |
string | e.g. Unknown Speaker 1 |
Upload Media
The media object takes a different shape depending on the upload's status, and is always
absent on transcript uploads. For any status not listed below, media is absent entirely.
Status: awaiting
| Field | Type | |
|---|---|---|
| status | One of: - awaiting- url_expired |
Status of the upload url |
| upload | object | Info for uploading the file |
| upload url |
string | URL to PUT the file to |
| upload expires_at |
ISO8601 formatted timestamp2026-01-01T09:30:00Z |
When the url expires |
| upload headers |
object | Headers required on the PUT request |
| upload max_duration_sec |
integer | Max duration in seconds the upload can be |
| upload max_upload_bytes |
integer | Max size in bytes the upload can be |
Status: processing
| Field | Type | |
|---|---|---|
| status | Always processing |
Status: staged
| Field | Type | |
|---|---|---|
| status | One of: - uploaded- media_expired |
Status of the uploaded file |
| expires_at | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
When the uploaded file expires |
Hook
| Field | Type | |
|---|---|---|
| id | UUID string | id of the hook |
| enabled | boolean | Whether the hook is enabled or not |
| hook_url | string | URL to which Grain will post to |
| hook_type | One of Hook Types | Type of notifications the hook will be sent. |
| include | object | The include the hook was created with.Always an empty object for hook_types with no include support (See Hook Types) |
| inserted_at | ISO8601 formatted timestamp2026-01-01T09:30:00Z |
UTC datetime of when the hook was created |
User
| Field | Type | |
|---|---|---|
| id | UUID string | id of the user |
| name | string | Name of the user |
| string | Primary email of the user |
Team
| Field | Type | |
|---|---|---|
| id | UUID string | id of the team |
| name | string | Name of the team |
Meeting Type
| Field | Type | |
|---|---|---|
| id | UUID string | id of the meeting type |
| name | string | Name of the meeting type |
| scope | One of: - internal- external |
Scope of the meeting type |
