Summaries API Reference
Reference for the operations an integration needs. For the end-to-end flow, see the Summaries API Guide.
Basics
| Endpoint | POST https://summaries.video.taxi/api/v1/graphql |
| Auth | Authorization: Bearer vts_... |
| Content type | application/json |
| GraphiQL | Open the endpoint URL in a browser. Set the Authorization header to run queries. |
| Introspection | Enabled |
Authentication
Create an API key in the web app under Account → API Tokens (/account/apitokens). The key is shown only once. It acts as the user who created it: it can see that user’s own summaries and the summaries of their organizations.
The Bearer prefix is optional. A bare vts_... value is also accepted.
Errors
GraphQL errors are returned with HTTP 200 in the errors array. The machine-readable code is in extensions.code.
{ "data": null, "errors": [{ "message": "Unauthorized access", "path": ["summary"], "extensions": { "code": "UNAUTHORIZED" } }]}| Code | Meaning |
|---|---|
UNAUTHORIZED | Missing or invalid API key. Also returned for summaries the key’s user cannot access. |
FORBIDDEN | Quota exhausted, or the operation is admin-only. |
NOT_FOUND | The referenced summary, version, summary type or organization does not exist. |
VALIDATION_ERROR | Invalid input or wrong state. See extensions.message. extensions.details holds a JSON-encoded string with per-item details. |
CONFLICT | Concurrent modification. |
SERVER_ERROR, DATABASE_ERROR, STORAGE_ERROR | Internal error. The message is always Internal server error. |
Scalars
| Scalar | Format |
|---|---|
UUID | "3f2c...-..." |
DateTime | RFC 3339, UTC |
JSON, JSONObject | Arbitrary JSON (value / object) |
Queries
me
The user the API key belongs to. You need id for the upload header.
query Me { me { id username email }}organizations
Organizations the user is a member of. Use id as organizationId when creating summaries.
query Organizations { organizations { id name }}summaryTypes
The available summary templates. id is the summaryTypeId used when creating a summary.
query SummaryTypes { summaryTypes { id label description templateId sortOrder disabledAt }}summary
One summary with one of its versions.
query Summary($id: UUID!, $version: Int!) { summary(id: $id, versionNumber: $version) { id title status lengthMs version { versionNumber status summaryTypeId } }}| Argument | Type | Description |
|---|---|---|
id | UUID! | Summary ID |
versionNumber | Int! | Version, starting at 1 |
Returns null if the summary does not exist. Returns NOT_FOUND if the version does not exist.
summaries
Paginated list of accessible summaries, each with its current version.
query Summaries($pagination: PaginationInput, $filter: SummaryFilter, $search: String) { summaries(pagination: $pagination, filter: $filter, search: $search) { totalCount hasNextPage summaries { id title status createdAt version { versionNumber status } } }}| Argument | Type | Description |
|---|---|---|
pagination | PaginationInput | offset (default 0), limit (default 50, max 100) |
filter | SummaryFilter | Filter by status, versionStatus, summaryTypeId, language |
search | String | Search term |
Mutations
generateUploadUrl
Creates a presigned URL for uploading one media file. Also reserves the summary ID.
mutation GenerateUploadUrl($input: GenerateUploadUrlInput!) { generateUploadUrl(input: $input) { id uploadId uploadUrl expiresInSeconds filename }}| Input field | Type | Description |
|---|---|---|
filename | String! | File name, e.g. meeting.mp3 |
contentType | String! | MIME type. Must equal the Content-Type of the upload. |
expiresInSeconds | Int | URL lifetime. Default 3600. |
| Result field | Description |
|---|---|
id | The summary ID for all later calls |
uploadId | Pass to completeUpload |
uploadUrl | Presigned S3 URL for the PUT |
filename | Name the file is stored under. Same as requested. |
Upload the file with:
curl -X PUT "$UPLOAD_URL" \ -H "Content-Type: audio/mpeg" \ -H "x-amz-meta-user-id: $USER_ID" \ -T meeting.mp3Both headers are part of the signature. USER_ID is me.id.
completeUpload
Registers the uploaded file on the summary.
mutation CompleteUpload($input: CompleteUploadInput!) { completeUpload(input: $input) { success summaryId message }}| Input field | Type | Description |
|---|---|---|
id | UUID! | id from generateUploadUrl |
uploadId | String! | uploadId from generateUploadUrl |
originalFilename | String! | Same filename as in generateUploadUrl |
language | String! | Recording language, e.g. de |
createSummaryAsync
Starts processing: demuxing, transcription, speaker and agenda prediction. Returns immediately.
mutation CreateSummary($summary: CreateSummaryInput!) { createSummaryAsync(summary: $summary) { summaryId versionNumber }}| Input field | Type | Description |
|---|---|---|
id | UUID! | id from generateUploadUrl |
title | String! | Display title |
language | String! | Recording language, e.g. de |
originalFilePath | String! | The uploaded file name (not a path), e.g. meeting.mp3 |
summaryTypeId | Int! | SummaryType.id |
organizationId | UUID | Organization that owns the summary. Omit for a personal summary. |
speakerList | [SpeakerInput!] | Known participants. Enables speaker prediction. |
speakerGroupId | UUID | Use the speakers of an existing speaker group. |
vocabularyIds | [UUID!] | Custom vocabularies for transcription |
agendaItems | [AgendaItemInput!] | Agenda items (TOPs). Council/parliament templates only. |
detectSpeakers | Boolean | Match voices against stored voice samples. Default false. |
versionNumber is always 1. The call is idempotent per id.
updateTranscript
Replaces the transcript of a version. Use it to set speaker names or correct text before summarizing.
mutation UpdateTranscript($update: UpdateTranscriptionInput!) { updateTranscript(update: $update)}| Input field | Type | Description |
|---|---|---|
summaryId | UUID! | Summary ID |
versionNumber | Int! | Version |
sentences | [UpdateTranscriptionSentence!]! | The complete transcript |
agendaAssignment | [AgendaAssignmentInput!]! | Agenda item positions. Send [] if unused. |
startSummarizing
Starts the LLM summarization for a version. Sets version.status to AWAITING_SUMMARY.
mutation StartSummarizing($id: UUID!, $version: Int!) { startSummarizing(summaryId: $id, versionNumber: $version, overrideTemplate: false, emptyTops: true)}| Argument | Type | Description |
|---|---|---|
summaryId | UUID! | Summary ID |
versionNumber | Int! | Version |
emptyTops | Boolean! | true if you do not use agenda items. See below. |
overrideTemplate | Boolean! | Only relevant with emptyTops: false. See below. |
agendaAssignments | [AgendaAssignmentInput!] | Agenda item positions in the transcript |
transcriptCheck | TranscriptCheckMode | Pre-check of the transcript. Default SKIP. |
Requires summary.status to be SPEAKER_PREDICTED or AGENDA_PREDICTED, otherwise VALIDATION_ERROR.
emptyTops and overrideTemplate select a variant of the council and parliament templates (de_council, de_council_meeting, de_parliament). For all other templates they have no effect.
emptyTops | overrideTemplate | Template used |
|---|---|---|
true | any | The selected template |
false | false | <template>_tops |
false | true | <template>_chapter |
transcriptCheck: STRICT rejects transcripts with too many speaker changes per section (VALIDATION_ERROR, detail code TRANSCRIPT_CHECK_FAILED). ACCEPT_WARNINGS_SWITCH_TEMPLATE starts anyway and may switch to a more robust template. Only council and parliament templates are checked.
changeSummaryVersionType
Changes the template of a version. Only possible while version.status is VOD_LAB_PENDING, i.e. before startSummarizing.
mutation ChangeType($id: UUID!, $version: Int!, $typeId: Int!) { changeSummaryVersionType(summaryId: $id, versionNumber: $version, newSummaryTypeId: $typeId)}createSummaryVersion
Creates a new version, e.g. to summarize the same recording with another template. Copies a transcript, inherits the summary type and becomes the current version.
mutation CreateVersion($input: CreateSummaryVersionInput!) { createSummaryVersion(input: $input) { summaryId versionNumber }}{ "input": { "summaryId": "...", "transcriptSource": { "version": { "versionNumber": 1 } } } }transcriptSource takes exactly one of original: {} (the unedited transcript) or version: { versionNumber }.
deleteVersion
Deletes one version and its transcript.
mutation DeleteVersion($id: UUID!, $version: Int!) { deleteVersion(id: $id, versionNumber: $version)}deleteSummary
Deletes a summary with all versions. Uploaded files are removed immediately.
mutation DeleteSummary($id: UUID!) { deleteSummary(id: $id)}Types
Only the fields relevant for integrations are listed.
Summary
type SummaryWithVersion { id: UUID! organizationId: UUID title: String! language: String! status: SummaryStatus! # transcription pipeline summaryTypeId: Int! lengthMs: Int # recording length createdAt: DateTime! failedAt: DateTime originalFileNames: [String!]! version: SummaryVersion! # the requested version availableVersions: [SummaryVersion!]! transcriptResult: TranscriptionResult summaryResult(applySelector: Boolean): SummaryResult errorMessage: String # always null for API keys}
type SummaryVersion { versionNumber: Int! status: SummaryVersionStatus! # summarization summaryTypeId: Int isCurrent: Boolean! createdAt: DateTime! completedAt: DateTime errorMessage: String # always null for API keys}Transcript
type TranscriptionResult { sentences: [TranscriptionSentence!]!}
type TranscriptionSentence { text: String! startMs: Int! # from start of recording endMs: Int! speakerId: String! # speaker label, e.g. "S1" or "Jane Doe"}speakerId is a free-text label, not an ID. The transcriber assigns S1, S2, … and you can replace them with names via updateTranscript.
Summary result
type SummaryResult { data: [JSONObject!] # summary sections error: String summaryDocx: String # internal, not downloadable via API key}data is a list of sections. Each section maps keys to arrays of values. The keys depend on the template, so render them generically. Every section also contains agendaItemId: [<UUID>] or [null].
Inputs
input SpeakerInput { firstName: String! lastName: String! title: String party: String function: String comment: String}
input AgendaItemInput { id: UUID! # client-generated title: String! sortOrder: Int! externalId: String # your own reference isPrivate: Boolean}
input UpdateTranscriptionSentence { text: String! startMs: Int! endMs: Int! speakerId: String!}
input AgendaAssignmentInput { title: String! startMs: Int! sentenceId: String!}Enums
enum SummaryStatus { IN_CREATION DRAFT DRAFT_FINALIZED DEMUXED TRANSCRIBED SPEAKER_PREDICTED AGENDA_PREDICTED FAILED}
enum SummaryVersionStatus { VOD_LAB_PENDING AWAITING_SUMMARY DONE FAILED}
enum TranscriptCheckMode { SKIP STRICT ACCEPT_WARNINGS_SWITCH_TEMPLATE}SummaryStatus | Meaning |
|---|---|
IN_CREATION, DRAFT | File uploaded, createSummaryAsync not yet called |
DRAFT_FINALIZED | Queued |
DEMUXED | Audio extracted |
TRANSCRIBED | Transcript available |
SPEAKER_PREDICTED | Speaker prediction done or skipped |
AGENDA_PREDICTED | Pipeline finished. Agenda prediction done or skipped. |
FAILED | Processing failed |
SummaryVersionStatus | Meaning |
|---|---|
VOD_LAB_PENDING | Waiting for startSummarizing (named after the web editor, “VOD Lab”) |
AWAITING_SUMMARY | Summarization running |
DONE | summaryResult available |
FAILED | Summarization failed |
Limits
| File size | Up to 2 GB recommended (web app limit). The API does not check the size, but files over 5 GB fail. |
| Formats | Video: mp4, m4v, mov, mpg, mpeg, ts, webm, 3gp, avi, flv, wmv, mxf. Audio: wav, mp3, m4a, aac, flac, opus, wma, gsm. |
| Language | de. Other codes are not validated and may fail transcription. |
| Page size | summaries: max 100 |
| Notifications | None for API clients. Poll. |
| Exports | DOCX/PDF downloads are only available in the web app. |