Skip to content

Media & Transcriptions

Use this guide to pick the right workflow, then follow the steps. All paths are session-scoped unless noted.

Playbook 1 — Sync transcript text

You already have the words (full transcript, summary, topics, or translation) and want them on a session.

Requires write:transcriptions to create; read:transcriptions to list or verify.

Playbook 2 — Upload a media file

You have a video or audio file (MP4, MOV, MP3, etc.) and want Sessionboard to transcribe it automatically.

Requires write:media to upload; read:media to check status. If you upload on a session URL, transcript lines appear in the session transcription list when processing finishes — no separate fragment upload needed.

Playbook 3 — Upload session audio

You have an audio file for a session’s recording archive (live capture, imported audio, or segments to stitch later).

Requires write:transcriptions. Accepts common audio formats; Sessionboard normalizes the file when you mark the upload complete.

Playbook 4 — Pull composed content

You want a ready-to-render pack for a mobile app or partner integration — composed transcript, summary, insights, podcast, and PDF links in one call.

Requires read:transcriptions. Prefer this over the raw polymorphic transcriptions list.


Replace {eventId} and {sessionId} with your event and session IDs. Send your API token on every request:

X-Access-Token: {your-token}

Create tokens and scopes in Authentication. Writes always need an explicit scope; legacy tokens with empty scopes can read but cannot create or upload.


  • You produced a transcript outside Sessionboard and want it on a session.
  • You need summaries, topic insights, or translations alongside transcript text.
typeWhat to send
fragmentTranscript text (transcription). Use one fragment for a full session transcript, or many for timed lines.
summarySummary content (content).
insightTopics or entities (name, insight_type). Optionally link to fragments with fragment_ids.
translationTranslated text (translated_text) linked to a source fragment or summary.

Create a transcription

POST /v1/event/{eventId}/sessions/{sessionId}/transcriptions
Content-Type: application/json

Example — full session transcript as one fragment:

{
"type": "fragment",
"transcription": "Welcome everyone to today's keynote...",
"provider": "my-platform",
"is_partial": false,
"language_code": "en"
}

Set is_partial to false when the text is final. The response includes the new id.

Verify on the session

GET /v1/event/{eventId}/sessions/{sessionId}/transcriptions
GET /v1/event/{eventId}/sessions/{sessionId}/transcriptions/{transcriptionId}

Update or remove (optional)

PUT /v1/event/{eventId}/sessions/{sessionId}/transcriptions/{transcriptionId}
DELETE /v1/event/{eventId}/sessions/{sessionId}/transcriptions/{transcriptionId}

To search across all sessions in an event:

GET /v1/event/{eventId}/transcriptions?page=1&page_size=25&type=fragment

By default, in-progress live-capture lines (is_partial=true) are hidden. Add include_partial=true to include them.


  • You have a video or audio file and want automatic transcription.
  • The file should appear in Sessionboard’s media hub for the session.
  1. You upload the file in parts (large files supported, up to 50 GB).
  2. Sessionboard processes the file and runs transcription.
  3. You poll until transcript_status is ready.
  4. If the upload URL includes {sessionId}, transcript fragments are added to that session automatically. You do not need Playbook 1 for the same content.

Start the upload

POST /v1/event/{eventId}/sessions/{sessionId}/media/upload/initiate
Content-Type: application/json
{
"filename": "keynote-recording.mp4",
"content_type": "video/mp4",
"size_bytes": 524288000
}

Response includes key, upload_id, and part sizing. Keep these for the next steps.

Get upload URLs for each part

POST /v1/event/{eventId}/sessions/{sessionId}/media/upload/sign-part
Content-Type: application/json
{
"key": "{key from initiate}",
"upload_id": "{upload_id from initiate}",
"part_numbers": [1, 2, 3]
}

Response returns a URL per part number.

Upload each part

PUT the file bytes to each URL from the previous step. Save the ETag header from each response — you need it to finish the upload.

Finish the upload

POST /v1/event/{eventId}/sessions/{sessionId}/media/upload/complete
Content-Type: application/json
{
"key": "{key}",
"upload_id": "{upload_id}",
"filename": "keynote-recording.mp4",
"size_bytes": 524288000,
"parts": [
{ "part_number": 1, "etag": "\"abc123\"" },
{ "part_number": 2, "etag": "\"def456\"" }
]
}

Response includes mediaItemId. The session is taken from the URL — do not send session_id in the body.

Poll until transcription is ready

GET /v1/event/{eventId}/sessions/{sessionId}/media/{mediaItemId}

Check transcript_status:

StatusMeaning
queuedWaiting to process
processingTranscription in progress
readyDone — session fragments are available if you uploaded on a session URL
failedProcessing failed — inspect the item or retry upload

Then list session transcriptions to read the generated fragments:

GET /v1/event/{eventId}/sessions/{sessionId}/transcriptions?type=fragment

  • You have audio for a session’s recording archive (not the media library flow in Playbook 2).
  • You need audio available for session playback, stitching, or downstream transcription inside Sessionboard.

wav, mp3, m4a, aac, flac, ogg, pcm

Start the recording upload

POST /v1/event/{eventId}/sessions/{sessionId}/recordings
Content-Type: application/json
{
"filename": "session-audio.wav",
"content_type": "audio/wav",
"size_bytes": 1048576
}

Response includes a recording id and an upload.url. Use that URL for the next step.

Upload the file

PUT the audio bytes to upload.url using the Content-Type from the response headers.

Mark the upload complete

POST /v1/event/{eventId}/sessions/{sessionId}/recordings/{recordingId}/complete
Content-Type: application/json
{
"duration_seconds": 3600,
"file_size_bytes": 1048576
}

Both fields are optional but help accuracy. Sessionboard accepts common formats and prepares the file for the recording pipeline when you complete.

Calling complete again on an already-completed recording returns success without duplicating work.

Confirm the recording

GET /v1/event/{eventId}/sessions/{sessionId}/recordings/{recordingId}
GET /v1/event/{eventId}/sessions/{sessionId}/recordings

  • You are building a mobile app, partner integration, or site that needs a ready-to-render transcript plus AI artifacts.
  • You want one call per session (or a paginated event list) instead of paging the mixed raw .../transcriptions list and stitching fragments yourself.

This is not session composition (abstract → parent/child linking). Content packs live under /content.

Requires read:transcriptions (legacy tokens with empty scopes still pass for reads).

Each session pack returns an items[] array. Filter by type:

typeMeaning
transcriptionComposed speaker turns (elements[]) plus full joined text
summaryPreferred rolling/final summary
insightThoughts, themes, topics, key phrases, etc.
podcastDigest audio status + time-limited url when ready
documentLinks to summary PDF / summary card downloads

Event-level packs add event_report and a recap rollup.

Get one session pack (canonical)

GET /v1/event/{eventId}/sessions/{sessionId}/content

Optional query params:

ParamPurpose
expandSlim the pack, e.g. expand=transcription,summary
interval_secRechunk same-speaker elements into ~N-second windows (default = speaker turns)
providerPrefer a summary provider (claude or openai) when multiple exist

Example response shape:

{
"data": {
"id": "{sessionId}",
"event_id": 6046,
"title": "Keynote",
"speakers": [{ "id": "0", "label": "Speaker 0", "name": null }],
"has_transcription": true,
"has_summary": true,
"has_podcast": false,
"items": [
{
"type": "transcription",
"text": "Full joined transcript…",
"elements": [
{
"value": "Speaker turn text…",
"speaker": { "id": "0", "label": "Speaker 0", "name": null },
"start_time": 2.23,
"end_time": 51.88
}
]
},
{ "type": "summary", "provider": "openai", "content": { "summary": "…", "key_points": [] } },
{ "type": "insight", "insight_type": "thought", "name": "…" },
{ "type": "podcast", "status": "none", "url": null },
{
"type": "document",
"document_type": "summary_pdf",
"url": "https://public-api.sessionboard.com/v1/event/{eventId}/sessions/{sessionId}/content/documents/summary_pdf"
}
]
}
}

Join transcript text in your client:

const transcription = data.items.find((i) => i.type === "transcription");
const fullText =
transcription?.text ??
transcription?.elements?.map((e) => e.value).join(" ") ??
"";

List packs across an event

GET /v1/event/{eventId}/content?page=1&page_size=25

Returns { data: [ SessionContent, … ], pagination: { current_page, page_size, total_pages, total_results } }.

List packs are compact by default (transcription text without full elements). Pass expand=items (with other types) for full element arrays:

GET /v1/event/{eventId}/content?page=1&page_size=25&expand=transcription,summary,insight,podcast,document,items

Get event-level artifacts

GET /v1/event/{eventId}/content/event

Returns the event report document metadata and a recap rollup (quotes, topics, summary excerpts). Default expand is document,recap.

Download a PDF or summary card (optional)

Use the url on a document item, or call directly:

GET /v1/event/{eventId}/sessions/{sessionId}/content/documents/summary_pdf
GET /v1/event/{eventId}/sessions/{sessionId}/content/documents/summary_card
GET /v1/event/{eventId}/content/documents/event_report

These stream binary PDF/PNG. Send the same X-Access-Token. Podcast url values are time-limited — refresh the content pack before expires_at.


ScopeUse it for
read:transcriptionsList/get transcriptions, recordings, and composed content packs
write:transcriptionsCreate, update, delete transcriptions; upload session audio
read:mediaCheck media upload and transcription status
write:mediaMultipart media upload

Scoped read tokens must include the matching read scope. For example, read:events alone does not allow transcription or media GET endpoints.


Get release notes by email

A short digest on the days something new goes live — each change with a link to its note in the release notes. No marketing, and one click to stop.

Company email addresses only. We’ll send one email to confirm it’s you. Already use Sessionboard? You can also turn this on under Community → What’s new.