Jobs
Submit media work and poll for results with a Job object — or configure webhooks to receive HTTPS callbacks on status changes instead of polling. Jobs are async — create returns with status: queued (or pending briefly if the job is not yet on Kafka). Poll until a terminal status (completed, failed, or cancelled), then fetch the result. Use POST /v1/jobs/{jobId}/cancel to stop in-flight work. Before creating a job, call GET /v1/jobs/policy?serviceType=… for plan-specific limits and supported client options.
Available service types:
transcription— transcribe a media file from S3 into timestamped segments. Result is a Transcript record accessible via the Transcripts API.clip_generation— analyze a long-form video and render short clips in a configurable layout (vertical, widescreen, square, or landscape-in-vertical) from an existing transcript. MP4s upload to your S3; Perchat stores clip metadata via the Clips API.thumbnail— generate 1–3 AI cover-image JPEGs from a completed transcription (maximum 3 per job). Images upload to your S3; metadata is stored on the transcript via the Thumbnails API.indexer— chunk, embed, and index a completed transcription into the Qdrant vector store so it can be searched via Semantic search. Requiresjob:create; the transcription must exist before submitting.metadata— generate title, description, IAB categories, and chapter markers from a completed transcript. Results are stored on the transcript via the Metadata API.summarization— synthesize a complete Summarization record (short/long synopsis, 3 SEO descriptions, 3 social hooks, 5 keywords, classification) using the transcript and visual chunks natively generated by atranscriptionjob. Create withPOST /v1/jobs(serviceType: summarization).
Progress event stages — transcription
Stage timelines are returned by GET /v1/jobs/{jobId}/progress (console session only), not on the Job object from retrieve or list. Each progress[] entry has stage, event (started | completed | failed | skipped), at, and optional details. Audio preprocessing stages (extract_audio, denoise_audio, separate_vocals) run automatically — not client-configurable.
| stage | Description |
|---|---|
download | Download source media |
transcribe | Top-level transcribe stage (wraps sub-stages below) |
extract_audio | Extract mono 16 kHz speech audio |
denoise_audio | Denoise + bandpass |
separate_vocals | Demucs vocal separation |
probe | Probe audio duration before chunking |
split_chunk | Split source into chunks (mp3 when input is audio-only after preprocessing, mp4 for video) |
compress_chunk | Compress chunk for upload |
transcribe_chunk | Transcribe one chunk |
merge | Merge chunk transcripts into one segment list |
transcribe_file | Transcribe whole file (non-chunked path) |
video_analysis | Visual frame analysis — GPT-4o mini vision samples frames and produces chunk summaries used downstream for summarization. Runs natively in every transcription job. |
upload_full_text | Upload full transcript text to S3 (s3_direct jobs) |
upload_segments | Upload segments JSON to S3 (s3_direct jobs) |
upload_transcript_srt | Upload SRT captions to S3 (s3_direct jobs) |
upload_transcript_vtt | Upload WebVTT captions to S3 (s3_direct jobs) |
Progress event stages — indexer
| stage | Description |
|---|---|
fetch_transcription | Fetch transcript segments from the API |
chunk | Split segments into overlapping text windows for embedding |
embed_sparse | BM25 sparse embedding (local, no API key) |
embed_dense | Dense embedding via OpenAI text-embedding-3-large |
index | Upsert new hybrid vectors (job-scoped point ids when re-indexing) |
cutover | After a successful upsert, remove prior index points for that transcription |
rollback | On failure, remove partial new vectors so the previous index stays searchable |
Progress event stages — clips
| stage | Description |
|---|---|
fetch_transcription | Load stored transcript segments from input.textSourceId (required) |
download | Fetch source video from storage connection |
ingest | Probe metadata, build proxy, extract audio |
detect_scenes | Scene boundary detection (PySceneDetect) |
analyze_scenes | GPT-4o vision per scene when enabled in platform settings; skipped otherwise |
build_timeline | Merge transcript, scenes, and vision into a timeline |
detect_category | Classify content genre from transcript and vision (GPT-4o-mini); skipped when categoryOverride is set, category detection is disabled in platform settings (analysis.category), or OpenAI is unavailable |
detect_candidates | Score and rank clip candidates |
render_clips | FFmpeg layout-aware reframe per candidate using the resolved job layout (vertical, widescreen, square, or landscape-in-vertical) |
upload_clips | Upload rendered MP4s to customer S3 |
Progress event stages — thumbnail
Each thumbnail job generates at most 3 JPEG variants. Set input.options.thumbnailCount to 1, 2, or 3 — values above 3 are rejected at validation. Outputs are written to your bucket via the job's storage connection.
Workers emit each row below in progress[]. A thumbnail_pipeline envelope event may also appear (started/completed); dashboards timeline the flat sub-stages, not the envelope.
| stage | Description |
|---|---|
download | Fetch source video from storage connection |
fetch_transcription | Load transcript segments from input.transcriptionId (required) |
thumbnail_ingest | Probe metadata and validate video readability |
thumbnail_encode | Downscale proxy video for faster frame extraction |
thumbnail_analyze | Chunk video, sample frames, score moments with gpt-4o vision |
thumbnail_merge | Consolidate scored frames and apply confidence threshold |
thumbnail_audio | Build narrative context from transcript for image prompts |
thumbnail_generate | Render JPEG variants via gpt-image-1 (imageQuality from platform config) |
upload_thumbnail_1/2/3 | Upload generated JPEGs to customer S3 (one stage per variant) |
Progress event stages — metadata
| stage | Description |
|---|---|
load_transcription | Fetch transcript segments from input.textSourceId (required) |
generate_metadata | LLM generates title, description, categories, and chapters (model from platform config) |
save_metadata | Persist Metadata record and embed on the transcription |
Progress event stages — summarization
| stage | Description |
|---|---|
synthesis | Call GPT-4o to synthesize chunk summaries and the full transcript into synopses, SEO descriptions, social hooks, keywords, and classification. |
Job failures & retries
When a job fails asynchronously, GET /v1/jobs/{jobId} still returns HTTP 200 — read failure fields on the Job object in data, not the top-level envelope error (that shape is for request/auth validation failures). See Errors → Job failures (async).
| Field | When set | Shape | Use for |
|---|---|---|---|
error | status: failed (terminal) | { code, message } | Primary integration signal — read error.code when polling ends in failed |
lastError | After any failed worker attempt (retry or terminal) | { at, message } — no code | Latest attempt message while a job is queued for retry; also present on terminal failed |
progress[].error | A stage event with event: failed | { code, message } | Console timeline UI — returned by GET /v1/jobs/{jobId}/progress, not on retrieve/list Job objects |
attemptHistory[] | When the worker requeues after a retryable failure | Array of attempt, failedAt, error, progress | Console-only via GET /v1/jobs/{jobId}/progress; use attemptCount and lastError when polling with API keys |
Retry lifecycle (retryable failures such as transient network errors):
- Worker attempt fails →
lastErroris set withat+message. - If retries remain:
statusreturns toqueued,attemptCountincrements, the failed attempt'sprogress[]is archived intoattemptHistory[], andprogress[]resets for the next run. Top-levelerrorstaysnulluntil the job is terminal. - Poll may show
status: queuedwith a non-nulllastError— the job is waiting to retry, not permanently failed. - After max retries or a non-retryable code (e.g.
duration_exceeded):status: failed, botherrorandlastErrorare populated. - On
status: completed, botherrorandlastErrorarenull.
While retrying (error still null):
{
"status": "queued",
"attemptCount": 1,
"lastError": {
"at": "2026-05-18T12:40:12Z",
"message": "Semantic search indexing could not connect to Qdrant. Configure the cluster in platform admin (Core → Qdrant): set the URL and API key, run Test connection, then save. Retry the job after Qdrant is reachable."
},
"error": null
}Terminal failure (error.code is the field to branch on):
{
"status": "failed",
"lastError": {
"at": "2026-05-18T12:34:58Z",
"message": "Video duration 7200.0s exceeds limit 3600s (checked at local_probe)"
},
"error": {
"code": "duration_exceeded",
"message": "Video duration 7200.0s exceeds limit 3600s (checked at local_probe)"
}
}Common error.code values and quotas: Plans & limits → Job failure codes. Full failed-job example on Retrieve a job.
A completed transcription job exposes resultSummary.textSourceId, segmentCount, and textPreview. A completed clips job exposes resultSummary.clipGenerationId and resultSummary.clipCount. A completed thumbnail job exposes resultSummary.textSourceId and resultSummary.thumbnailCount and resultSummary.nonAiThumbnailCount. A completed metadata job exposes resultSummary.metadataId and resultSummary.chapterCount. Use the Transcripts API for full text, segments, word timings, and editing — not the Job object itself. Use the Clips API for per-clip metadata after clip jobs complete. Use the Thumbnails API for per-image metadata after thumbnail jobs complete. Use the Metadata API for title, description, categories, and chapters after metadata jobs complete. A completed indexer job exposes resultSummary with textSourceId, collection, and indexedChunks. Use the Catalog API to search indexed chunks after indexer jobs complete.
Transcription flow
POST /v1/jobs(serviceType: transcription) — submit using S3 storage connectionGET /v1/jobs/{jobId}— poll untilstatus: completedGET /v1/transcriptions/{transcriptionId}— fetch the transcript (useresultSummary.textSourceIdfrom the completed job)- Optional: Transcripts edit/glossary endpoints if you build a review UI
Clips flow
- Complete a transcription job — note the
textSourceIdfromresultSummary - Optional:
GET /v1/jobs/policy?serviceType=clip_generation— read supported layouts, caps, and client options for your plan POST /v1/jobs(serviceType: clip_generation) withinput.textSourceIdand optionalinput.options.maxClips,layout,fillStyle,categoryOverride— S3 storage connectionGET /v1/jobs/{jobId}— poll untilstatus: completedGET /v1/clip-generations/{clipGenerationId}— fetch per-clip metadata and segment spans (useresultSummary.clipGenerationIdfrom the completed job)
Thumbnail flow
- Complete a transcription job first — thumbnail jobs require
input.textSourceIdwith segments. POST /v1/jobs(serviceType: thumbnail) — S3 storage connection; setinput.options.thumbnailCount(1–3, maximum 3 per job).GET /v1/jobs/{jobId}— poll untilstatus: completedGET /v1/transcriptions/{transcriptionId}(includesthumbnails) orGET /v1/thumbnails/{transcriptionId}— fetch per-image metadata
Metadata flow
- Complete a transcription job — note the
textSourceIdfromresultSummary POST /v1/jobs(serviceType: metadata) withinput.textSourceId— Create a metadata jobGET /v1/jobs/{jobId}— poll untilstatus: completedGET /v1/transcriptions/{transcriptionId}/metadata— fetch generated fields
Semantic search flow
- Complete a transcription job — note the
textSourceIdfromresultSummary POST /v1/jobs(serviceType: indexer) withinput.textSourceIds— poll untilstatus: completedPOST /v1/catalog/search— query across all indexed transcriptions in the project (requiresai:searchpermission)
Summarization flow
- Complete a transcription job — note the
jobIdfromresultSummary. The transcription pipeline automatically generates video analysis chunks. POST /v1/jobs(serviceType: summarization) withinput.textSourceJobIdGET /v1/jobs/{jobId}— poll the summarization job untilstatus: completedGET /v1/summarizations/{summarizationId}— fetch the Summarization record usingresultSummary.summarizationIdfrom the completed job
Requires a project API key (sk_prj_…) with job:* permissions. Queue priority is set from your workspace plan — do not pass it on create.
See also: Jobs & transcripts workflows for end-to-end patterns (poll vs list, export, custom editor). Quotas, max video length (1 hour), and failure codes: Plans & limits.
The Job object
- jobIdstring
- Unique identifier (job_…).
- serviceTypestring
- Service to run:
transcription,clip_generation,thumbnail,metadata,indexer, orsummarization. - statusstring
pending(not yet on the queue),queued,processing,cancelling(graceful stop in progress),completed,failed, orcancelled.- prioritystring
- Queue lane:
low,normal, orhigh. Set from workspace plan at creation — not client-controlled. - workspaceIdstring
- Owning workspace.
- projectIdstring
- Project the job belongs to.
- workspacePlanstring, optional
- Workspace plan snapshotted at job creation (
free,pro, etc.). Drives queuepriority. - inputobject
- Validated transcription input via
s3_direct—connectionId,bucket,sourceKey, optionaloutputPrefix(uses a project storage connection). The create response includesinput.textSourceIdand resolved output paths (resolvedOutputPrefix,resolvedOutputKeys).Both modes accept optionaloptions.language. Stored jobs also include platform-managedoptions.preprocessing. Fors3_direct, resolved S3 output paths are returned on the jobinputat create. - resultSummaryobject | null, optional
- Shape depends on
serviceType:- Transcription —
nullat create and while the job is in flight. When complete:textSourceId,textPreview,segmentCount,durationSec. Fors3_direct,input.textSourceIdis already set at create (same id asresultSummary.textSourceIdafter success). SavejobIdfrom the create response and pollGET /v1/jobs/{jobId}(or use webhooks) to obtainresultSummary.textSourceIdonce the transcript record exists. - Clips — when complete:
clipGenerationId,clipCount,sourceDurationSec,transcriptPreview. - Thumbnail — when complete:
textSourceId,thumbnailCount(accumulated total),newThumbnailCount(added by this run),nonAiThumbnailCount,generated. - Metadata — when complete:
textSourceId,metadataId,title,chapterCount,generated. - Indexer — when complete:
textSourceId,collection,indexedChunks,denseEngine,sparseEngine, optionalchunkType. - Summarization — when complete:
summarizationIdandshortSynopsisPreview(first 100 characters of the short synopsis). Fetch the full Summarization record viaGET /v1/summarizations/{summarizationId}.
nullwhile pending, queued, processing, or cancelling. - Transcription —
- cancelRequestedAttimestamp | null, optional
- When cancellation was requested; null until a cancel call.
- cancelRequestedByLabelstring | null, optional
- Who requested cancel (@user, API key name, or Console).
- cancelRequestedByKindstring | null, optional
user,api_key, orconsolewhen cancel was requested.- cancelRequestedByUserIdstring | null, optional
- User id when cancel was requested from the dashboard.
- cancelRequestedByApiKeyIdstring | null, optional
- API key id when cancel was requested with a project key.
- cancelModestring | null, optional
- Currently
graceful— processing jobs stop after the current pipeline stage. - sourceDisplayNamestring, optional
- Human-readable source label derived from
input(e.g. filename without extension). Used in job list search and dashboards. - webhookUrlstring | null, optional
- Per-job HTTPS webhook override from create, when set. Otherwise
nulland the project default from Webhooks applies. - currentStagestring | null, optional
- Worker stage in flight (e.g. transcribe); null when idle or finished.
- attemptCountinteger, optional
- Failed attempts before this run; 0 on first try. Incremented on retry.
- lastErrorobject | null, optional
- Snapshot of the most recent failed worker attempt:
at(timestamp) andmessage— nocodefield. Set while a job is retrying (status: queued) or on terminalfailed;nullon success and on jobs that never failed. See Job failures & retries. - errorobject | null
- Terminal failure reason when
statusisfailed:code(e.g.duration_exceeded,transcript_too_large,processing_failed) andmessage. Otherwisenull— including while retrying (queuedwith a non-nulllastError). Not the HTTP envelopeerrorfield. See Job failures & retries. - createdByLabelstring, optional
- Human-readable creator (@user, API key name, or Console). On retrieve/list.
- createdByKindstring, optional
- One of
user,api_key,console. - apiKeyLabelstring | null, optional
- Name of the API key that created the job, when applicable.
- apiKeyIdstring, optional
- API key or console pseudo-key that owns the job.
- isConsoleKeyboolean, optional
- True when submitted from the dashboard browser session.
- createdAttimestamp
- Creation time.
- updatedAttimestamp
- Last update time.
- startedAttimestamp | null, optional
- When processing began.
- completedAttimestamp | null, optional
- When the job finished (success, failure, or cancel).
- userMetadataobject, optional
- Caller-defined metadata from job creation. Transcription jobs typically include
customerJobRef(your reference string).
Job policy
/v1/jobs/policyserviceType — sourced from platform admin settings. No authentication required. Pass the same serviceType you use on POST /v1/jobs.Parameters
- serviceTypestring
- Required. One of
clip_generation,transcription,thumbnail, ortranslation.
Returns
serviceType-specific payload. Plan-scoped services include plans.free and plans.enterprise— read the entry that matches the caller's workspace plan. Example for clip jobs below; transcription, thumbnail, and translation shapes differ.See the the job object for attribute details.
Create a transcription job
/v1/jobsNote: The transcription pipeline also natively performs visual frame analysis to produce chunk summaries for use in summarization.
When to use
- Reusable bucket access — you configured S3 credentials once under Project → Storage.
- Simpler job payloads — four fields instead of five presigned URLs per job.
- Same outputs — full text, segments JSON, SRT, and VTT are written to your bucket under the resolved output location (from
outputPrefixwhen set, or{defaultOutputPrefix}/{textSourceId}/transcripts/when omitted — see parameter notes).
Requires permission
job:create
priority — queue lane is set from your workspace plan at creation. Do not send input.options.preprocessing — extract and denoise run by default; vocal separation runs only when enabled in platform settings. Transcription engine is platform-managed and not exposed on job objects. Send userMetadata.customerJobRef so you can correlate jobs in your systems and filter the job list; the console auto-fills this from your input settings.Save
jobId as your async handle. Transcription is asynchronous — the create response has resultSummary: null because the transcript record does not exist yet. For s3_direct jobs, input.textSourceId is also assigned at create (used for the default output path and downstream jobs). When the job finishes, poll GET /v1/jobs/{jobId} (see Retrieve a job) or handle a job status webhook (event: completed) and read resultSummary.textSourceId (same id as input.textSourceId on s3_direct). Use that id with GET /v1/transcriptions/{transcriptionId} for segment text, editing, and downstream jobs (clips, thumbnails, metadata, indexer, or summarization).Getting the transcription ID
- At create (
s3_direct) —input.textSourceIdis returned on the job object immediately (alongsidejobId);resultSummaryis stillnull. - At create (
s3_presigned) — onlyjobIdis returned;resultSummaryisnull. - After completion — poll
GET /v1/jobs/{jobId}untilstatus: completed, then readresultSummary.textSourceId(or read it from acompletedwebhook payload). - Correlate jobs — pass
userMetadata.customerJobRefat create and filter withGET /v1/jobs?customerJobRef=…if you lose thejobId.
S3-only result path. The worker writes full text, segments JSON, SRT, and VTT to your bucket at the resolved output location. When omitted, outputs are written under
perchat-media-results/{transcriptionId}/transcripts/ (or {defaultOutputPrefix}/{transcriptionId}/transcripts/ when the bucket's defaultOutputPrefix is set on the storage connection). Files are placed directly in that folder (e.g. full_text.txt, clip_01.mp4, thumbnail_1.jpg). Set outputPrefix to override with a custom folder path — it is not combined again with defaultOutputPrefix. If your pipeline only reads those files from S3, you can use input.textSourceId from the create response to locate them. Use the Transcripts API (and downstream jobs such as clips or indexer) when you need platform-managed transcript records.Create a storage connection first (dashboard or POST storage connection with a project API key that includes
storage_connection:create). Create and update run automatic bucket permission tests; inactive connections are rejected.Example body:
{
"serviceType": "transcription",
"input": {
"sourceType": "s3_direct",
"connectionId": "e7b2c4d8-0000-4000-a000-000000000001",
"bucket": "my-bucket",
"sourceKey": "media/clips/demo.mp4",
"options": {
"language": "en"
}
},
"userMetadata": {
"customerJobRef": "abc-123"
}
}Validation at createconnectionIdmust belong to the project bound to your API key;bucketmust be on the connection.sourceKeymust reference a supported video file; the API probes the object with a ranged GET.- When
outputPrefixis set, it is the full output location in the bucket (not combined again with the connection'sdefaultOutputPrefix). When omitted, the API resolves{defaultOutputPrefix}/{textSourceId}/transcripts/using the pre-assignedinput.textSourceIdand the bucket'sdefaultOutputPrefix(defaults toperchat-media-results). - Output paths are validated with empty PUT probes on the resolved keys before the job is queued.
- Failures return 422
validationErrorwitherror.details[].
Parameters
- serviceTypestring
- Must be
transcription. - userMetadataobject, optional
- Caller-defined metadata echoed on the Job object. For transcription jobs, include
customerJobRef(see below). The object may be omitted on the REST API, but the Perchat console always sends it. - userMetadata.customerJobRefstring, optional
- Your stable reference for this job (e.g. internal ticket id or
s3_direct-my-bucket-demo-en). Used to filter jobs viaGET /v1/jobs?customerJobRef=…. Required in the Perchat console; strongly recommended for API integrations (max 120 characters; letters, digits, hyphen, underscore). Echoed on webhook payloads when set. - webhookUrlstring, optional
- Optional HTTPS URL for job status callbacks on this job only. Overrides the project default from Webhooks. Must use
https://. - input.sourceTypestring
- Must be
s3_direct. - input.connectionIdstring
connectionIdfrom a project Storage connection. The worker uses stored credentials — no presigned URLs.- input.bucketstring
- S3 bucket name. Must be listed on the connection's
bucketsarray. - input.sourceKeystring
- Object key for the source video (e.g.
media/clips/demo.mp4). Must have a supported video extension. - input.outputPrefixstring, optional
- Optional output location for transcript files (
s3_direct; API field:outputPrefix). When set, used as the full relative path in the bucket (no leading slash, no..segments). When omitted, outputs are written underperchat-media-results/{transcriptionId}/transcripts/(or{defaultOutputPrefix}/{transcriptionId}/transcripts/when the bucket'sdefaultOutputPrefixis set on the storage connection). Files are placed directly in that folder (e.g.full_text.txt,clip_01.mp4,thumbnail_1.jpg). SetoutputPrefixto override with a custom folder path — it is not combined again withdefaultOutputPrefix. - input.options.languagestring, optional
- Optional ISO language hint (e.g.
en,ta). Omitted = auto-detect.
Returns
status: queued with resultSummary: null. Save jobId as your async handle. The response includes input.textSourceId (pre-assigned for output paths and downstream jobs). The worker uploads transcript files to your bucket on success — see Retrieve a job.See the the job object for attribute details.
Create a translation job
/v1/jobsWhen to use
- After transcription completes — pass the completed
textSourceId. - Language code required — send codes such as
ta,hi,ja,es, oren-US. Do not send display names such asTamil. - No transcript payload — the platform fetches segments from the existing transcript using
input.textSourceId.
Requires permission
job:create
{
"serviceType": "translation",
"input": {
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"targetLanguage": "ta",
"sourceLayer": "current"
},
"userMetadata": {
"customerJobRef": "translation-demo-ta"
}
}Completed translation job:{
"jobId": "job_01HZ2K9V3JEK4WYZN6F0XQ2W9N",
"serviceType": "translation",
"status": "completed",
"priority": "low",
"workspaceId": "wsp_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"projectId": "prj_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"workspacePlan": "free",
"input": {
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"targetLanguage": "ta",
"sourceLanguage": "en",
"sourceLayer": "current",
"config": {
"backend": "openai"
}
},
"resultSummary": {
"translationId": "trl_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"sourceLanguage": "en",
"targetLanguage": "ta",
"languageKey": "ta",
"sourceLayer": "current",
"segmentCount": 152,
"durationSec": 318.08,
"translationBackend": "openai",
"model": "gpt-4o"
},
"sourceDisplayName": "demo",
"webhookUrl": null,
"currentStage": null,
"attemptCount": 0,
"lastError": null,
"error": null,
"createdAt": "2026-05-18T12:34:56Z",
"updatedAt": "2026-05-18T12:36:22Z",
"startedAt": "2026-05-18T12:34:57Z",
"completedAt": "2026-05-18T12:36:22Z",
"userMetadata": { "customerJobRef": "translation-demo-ta" }
}The worker writes the translated segment array to translation.<languageKey> on the transcript and also persists the full Translation record. Re-running the same target language replaces that language array; translation revision history is not stored.
Parameters
- serviceTypestring
- Must be
translation. - input.textSourceIdstring
- Preferred — completed text source ID (
txt_…) to translate. Must belong to the same project as the API key. - input.targetLanguagestring
- Target language code, for example
ta,hi,ja,es, oren-US. - input.sourceLanguagestring, optional
- Optional source language code. Omit when the source language should be inferred from the transcript/default.
- input.sourceLayerstring, optional
- Optional transcript layer to translate:
current,glossary, orraw. Defaults tocurrent, which points tosegments. - userMetadataobject, optional
- Optional caller-defined metadata echoed on the Job object. Include
customerJobReffor stable lookup in job lists.
Returns
status: queued with resultSummary: null. When complete, resultSummary includes translationId, language info, source layer, segment count, backend, and model. Retrieve the full Translation record from the Translations API.See the the job object for attribute details.
Create a dubbing job
/v1/jobsWhen to use
- After transcription completes — use the
textSourceIdfrom the transcription job'sresultSummary.
Requires permission
job:create
{
"serviceType": "dubbing",
"input": {
"transcriptionId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"targetLanguage": "ta",
"sourceLanguage": "en",
"sourceLayer": "current",
"options": {
"diarization": {
"enabled": true,
"minSpeakers": 1,
"maxSpeakers": 4
},
"voiceAssignments": {
"SPEAKER_00": "vce_01HX2K9V3JEK4WYZN6F0XQ2W8M"
}
}
},
"userMetadata": {
"customerJobRef": "dubbing-demo-ta"
}
}Completed dubbing job:{
"jobId": "job_01HZ2K9V3JEK4WYZN6F0XQ2W9N",
"serviceType": "dubbing",
"status": "completed",
"priority": "low",
"workspaceId": "wsp_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"projectId": "prj_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"workspacePlan": "free",
"input": {
"transcriptionId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"targetLanguage": "ta",
"sourceLanguage": "en",
"sourceLayer": "current",
"options": {
"diarization": { "enabled": true, "minSpeakers": 1, "maxSpeakers": 4 },
"voiceAssignments": { "SPEAKER_00": "vce_01HX2K9V3JEK4WYZN6F0XQ2W8M" }
}
},
"resultSummary": {
"dubbingId": "dub_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"transcriptionId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"translationId": "trl_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"sourceLanguage": "en",
"targetLanguage": "ta",
"languageKey": "ta",
"textPreview": "என்ன விஷயம், FitFam?...",
"segmentCount": 152,
"durationSec": 318.08
},
"sourceDisplayName": null,
"webhookUrl": null,
"currentStage": null,
"attemptCount": 1,
"lastError": null,
"error": null,
"createdAt": "2026-05-18T12:34:56Z",
"updatedAt": "2026-05-18T12:45:22Z",
"startedAt": "2026-05-18T12:34:57Z",
"completedAt": "2026-05-18T12:45:22Z",
"userMetadata": { "customerJobRef": "dubbing-demo-ta" }
}The worker writes the full Dubbing record with artifacts for raw audio, vocals, background, translated speech, final audio mix, and final dubbed video. For the first controlled test, provide manual voice assignments to reuse existing voice profiles.
Automatic voice creation inside the dubbing pipeline is internal to dubbing and does not consume the public voice_creation allowance. If no manual, source-transcript, or auto-created voice is available for a speaker, the worker marks that speaker for manual assignment and skips TTS for the affected segments instead of using a fallback voice.
Parameters
- serviceTypestring
- Must be
dubbing. - input.transcriptionIdstring
- Completed source transcript ID (
txt_...) to dub. Dubbing uses transcription text sources, not narration sources. - input.targetLanguagestring
- Target language code for the dubbed output, for example ta, hi, es, ja, or en-US.
- input.sourceLanguagestring, optional
- Optional source language code. Omit when it should be inferred from the transcript/default.
- input.sourceLayerstring, optional
- Optional transcript layer to use:
current,glossary, orraw. Defaults tocurrent. - input.translationIdstring, optional
- Optional Translation record id to associate with this dubbing request.
- input.options.diarization.enabledboolean, optional
- Enable speaker diarization and speaker assignment. Defaults to true.
- input.options.diarization.minSpeakersinteger, optional
- Optional minimum speaker count hint.
- input.options.diarization.maxSpeakersinteger, optional
- Optional maximum speaker count hint.
- input.options.voiceAssignmentsobject, optional
- Optional speaker-to-voice mapping. Keys are diarization labels such as
SPEAKER_00; values are existing Perchat voice IDs such asvce_.... - input.options.voiceCloning.enabledboolean, optional
- Enable automatic voice creation from extracted speaker samples when no manual voice assignment is provided. Defaults to true.
- input.options.tts.enabledboolean, optional
- Enable TTS generation for dubbing segments. Defaults to true.
- input.options.tts.modelIdstring, optional
- Optional provider TTS model override when supported by the configured voice service.
- userMetadataobject, optional
- Optional caller-defined metadata echoed on the Job object. Include
customerJobReffor stable lookup in job lists.
Returns
status: queued with resultSummary: null. When complete, resultSummary includes dubbingId, transcriptionId, language info, segment count, and duration. Retrieve the full Dubbing record from the Dubbings API.See the the job object for attribute details.
Create an indexer job
/v1/jobsWhen to use
- After transcription completes — use the
transcriptionIdfrom the transcription job'sresultSummary.
Requires permission
job:create
{
"serviceType": "indexer",
"input": {
"textSourceIds": [
"txt_01HX2K9V3JEK4WYZN6F0XQ2W8M"
]
},
"userMetadata": {
"customerJobRef": "indexer-01HX2K9V3JEK4WYZ"
}
}Batch (up to 50 IDs):{
"serviceType": "indexer",
"input": {
"textSourceIds": [
"txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"txt_02HX2K9V3JEK4WYZN6F0XQ2W8N"
]
},
"userMetadata": {
"customerJobRef": "indexer-batch-q3-library"
}
}Completed indexer job:{
"jobId": "job_02HX2K9V3JEK4WYZN6F0XQ2W8M",
"serviceType": "indexer",
"status": "completed",
"priority": "low",
"workspaceId": "wsp_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"projectId": "prj_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"input": {
"textSourceIds": [
"txt_01HX2K9V3JEK4WYZN6F0XQ2W8M"
]
},
"resultSummary": {
"indexedTranscriptions": [
{
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"segmentCount": 80,
"indexedChunks": 8,
"removedStaleChunks": 0,
"reindexed": false
}
],
"collection": "perchat_proj_prj_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"indexedChunks": 8,
"denseEngine": "openai/text-embedding-3-large",
"sparseEngine": "bm25/fastembed",
},
"currentStage": null,
"attemptCount": 0,
"lastError": null,
"error": null,
"createdAt": "2026-05-18T12:40:00Z",
"updatedAt": "2026-05-18T12:40:06Z",
"startedAt": "2026-05-18T12:40:01Z",
"completedAt": "2026-05-18T12:40:06Z",
"userMetadata": null
}One collection is maintained per project (perchat_proj_{projectId}). Re-indexing writes new vectors first, then removes the previous index on success. If indexing or cutover fails, the worker rolls back partial new vectors and the job fails with error.code: index_rollback — the prior index remains searchable. Pass input.textSourceIds(array, including one ID). Each ID consumes one indexing credit when the job is created (see Plans & limits).
Transcription reference validation (at create)
- The API checks that each ID exists in the current project (API key / console scope) and has at least one stored segment before the job is queued. Invalid references are rejected synchronously — no credits are consumed.
- 404
notFound— unknown ID or transcript in another project. Message:transcription not found: … - 422
validationError— transcript exists but has no segments (segmentCountis 0 and no segment payload). Message:transcription txt_01HX… has no segments. error.details[]includespathandissuefor integration fixes. See Errors.
Example (not found):
{
"error": {
"code": "notFound",
"message": "transcription not found: txt_01HX…",
"details": [ { "path": "input.textSourceIds", "issue": "notFound:txt_01HX…" } ]
}
}Example (no segments):
{
"error": {
"code": "validationError",
"message": "transcription txt_01HX… has no segments",
"details": [ { "path": "input.textSourceIds", "issue": "noSegments:txt_01HX…" } ]
}
}Parameters
- serviceTypestring
- Must be
indexer. - input.transcriptionIdsarray
- One or more transcription IDs to index (txt_...), up to 50 per job. Deduped server-side. Use a one-element array for a single transcript.
- userMetadataobject, optional
- Optional caller-defined metadata echoed on the Job object. Include
customerJobRefso search hits and index inventory can show your reference on each chunk. - userMetadata.customerJobRefstring, optional
- Stable reference for this indexer job (max 120 characters). Echoed on search hits and index inventory as
customerJobRef. If omitted, the platform may suggest a value such asindexer-{transcriptionIdSuffix}.
Returns
status: queued. When complete, resultSummary includes transcriptionId, collection, indexedChunks, denseEngine, and sparseEngine.See the the job object for attribute details.
Create a metadata job
/v1/jobsWhen to use
- Prerequisite — a completed transcription job; pass its
textSourceIdon create. - No S3 input — metadata jobs only require
input.textSourceId; the worker reads transcript text from the API.
Requires permission
job:create
{
"serviceType": "metadata",
"input": {
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M"
},
"userMetadata": {
"customerJobRef": "metadata-01HX2K9V3JEK4WYZ"
}
}Completed metadata job:{
"jobId": "job_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"serviceType": "metadata",
"status": "completed",
"resultSummary": {
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"metadataId": "mdt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"title": "Introduction to Web Application Development",
"chapterCount": 3,
"generated": true
},
"input": {
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M"
},
"createdAt": "2026-05-29T10:00:00Z",
"startedAt": "2026-05-29T10:00:01Z",
"completedAt": "2026-05-29T10:00:18Z"
}One metadata credit is consumed when the job is created (see Plans & limits). LLM model and step timeouts are configured in platform admin (Jobs → Metadata) and snapshotted on the worker at runtime — do not pass model options on create.
Transcription reference validation (at create)
- The API checks that each ID exists in the current project (API key / console scope) and has at least one stored segment before the job is queued. Invalid references are rejected synchronously — no credits are consumed.
- 404
notFound— unknown ID or transcript in another project. Message:transcription not found: … - 422
validationError— transcript exists but has no segments (segmentCountis 0 and no segment payload). Message:transcription txt_01HX… has no segments. error.details[]includespathandissuefor integration fixes. See Errors.
Example (not found):
{
"error": {
"code": "notFound",
"message": "transcription not found: txt_01HX…",
"details": [ { "path": "input.textSourceId", "issue": "notFound:txt_01HX…" } ]
}
}Example (no segments):
{
"error": {
"code": "validationError",
"message": "transcription txt_01HX… has no segments",
"details": [ { "path": "input.textSourceId", "issue": "noSegments:txt_01HX…" } ]
}
}Parameters
- serviceTypestring
- Must be
metadata. - input.textSourceIdstring
- Completed text source ID (
txt_…) to generate metadata for. Must belong to the same project as the API key. - userMetadataobject, optional
- Optional caller-defined metadata echoed on the Job object. Include
customerJobReffor stable lookup in job lists. - userMetadata.customerJobRefstring, optional
- Stable reference for this metadata job (max 120 characters). Typical pattern:
metadata-{textSourceIdSuffix}.
Returns
status: queued. When complete, resultSummary includes metadataId, title, and chapterCount.See the the job object for attribute details.
Create a summarization job
/v1/jobsinput.textSourceJobId (job_…).When to use
- Prerequisite — a completed transcription job in the same project.
- No S3 input — the worker reads transcript text and visual chunks from stored records.
Requires permission
job:create
{
"serviceType": "summarization",
"input": {
"textSourceJobId": "job_01HX2K9V3JEK4WYZN6F0XQ2W8M"
},
"userMetadata": {
"customerJobRef": "summarization-job_01HY2K9V3JEK4WYZ"
}
}Completed summarization job:{
"jobId": "job_01HZ2K9V3JEK4WYZN6F0XQ2W9N",
"serviceType": "summarization",
"status": "completed",
"priority": "low",
"attemptCount": 0,
"lastError": null,
"input": {
"textSourceJobId": "job_01HX2K9V3JEK4WYZN6F0XQ2W8M"
},
"resultSummary": {
"summarizationId": "sum_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"shortSynopsisPreview": "A high-energy music video featuring vibrant choreography across neon-lit urban locations..."
},
"error": null,
"startedAt": "2026-05-29T10:00:01Z",
"completedAt": "2026-05-29T10:00:22Z",
"processingLeaseUntil": null,
"cancelRequestedAt": null,
"cancelRequestedByUserId": null,
"cancelRequestedByApiKeyId": null,
"cancelRequestedByLabel": null,
"cancelRequestedByKind": null,
"cancelMode": null,
"currentStage": null,
"userMetadata": {
"customerJobRef": "summarization-job_01HY2K9V3JEK4WYZ"
},
"webhookUrl": null,
"workspacePlan": "free",
"workspaceId": "wsp_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"projectId": "prj_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"apiKeyId": "pak_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"createdAt": "2026-05-29T10:00:00Z",
"updatedAt": "2026-05-29T10:00:22Z",
"createdByLabel": "Test Summarization",
"createdByKind": "api_key",
"apiKeyLabel": "Test Summarization",
"isConsoleKey": false,
"sourceDisplayName": "test"
}Stored job input.config reflects platform settings at create time (read-only on the job object). Summarization credits are charged when the job is accepted — 1 credit per summarization. Failed jobs after queueing still count. The synthesis model and timeouts are platform-managed — do not pass model options on create.
Transcription reference validation (at create)
input.textSourceJobIdis required and must match a transcript in this workspace and project:txt_…— lookup by transcription ID → 404transcription not found: txt_…if missing.job_…— lookup by transcription job ID → 404transcription not found for job: job_…if missing.
- 422
validationErrorwheninput.textSourceJobIdis missing or empty. - 422
validationErrorwhen the resolved transcript has no segments. error.details[]usespath: textSourceJobIdandissue: notFoundornoSegments.
Parameters
- serviceTypestring
- Must be
summarization. - input.textSourceJobIdstring
- Completed text source job ID (
job_…). Must belong to the same project as the API key. - userMetadata.customerJobRefstring, optional
- Stable reference for this summarization job (max 120 characters). Typical pattern:
summarization-{textSourceJobIdSuffix}.
Returns
status: queued. When complete, resultSummary includes summarizationId and shortSynopsisPreview. Fetch the full record via GET /v1/summarizations/{summarizationId}.See the the job object for attribute details.
Create a clips job
/v1/jobsWhen to use
- Prerequisite — a completed transcription job; pass its
textSourceId(txt_…) on create. - Policy — call
GET /v1/jobs/policy?serviceType=clip_generationbefore create to readmaxClipsDefault,maxClipsCap, supported layouts, fill styles, categories,allowAutoLayout, and whethercategoryOverrideis allowed. - S3 storage connection — use a saved storage connection; the API resolves clip output keys under
{defaultOutputPrefix}/{textSourceId}/clips/unlessoutputPrefixis set. Source fields can be omitted whentextSourceIdis set — they are resolved from the transcription job.
Requires permission
job:create
textSourceId (txt_…) — clip jobs reuse stored segments and never run Whisper.Client options (all optional — must be allowed on your plan; live allowlists: GET /v1/jobs/policy?serviceType=clip_generation):
maxClips— defaults tomaxClipsDefault, capped atmaxClipsCap(1 credit per clip at job create).layout— one output layout for all clips in the job:vertical_9_16(short-form vertical),horizontal_in_vertical(landscape in vertical frame),horizontal_16_9(widescreen), orsquare_1_1(square). Omit to use the platform default or auto-select from detected content category whenallowAutoLayoutistrueon your plan.fillStyle— letterbox fill when layout ishorizontal_in_vertical:black,blur, orbranding. Defaults to the platform render default.categoryOverride— force content type (for exampleinterview,podcast,gaming) instead of auto-detection; skipsdetect_categorywhen set.
Do not send input.options.analysis — vision and category toggles are platform-managed and snapshotted into the stored job at create.
{
"serviceType": "clip_generation",
"input": {
"sourceType": "s3_direct",
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"connectionId": "stc_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"bucket": "my-media-bucket",
"sourceKey": "media/clips/demo.mp4",
"options": {
"maxClips": 5,
"layout": "horizontal_in_vertical",
"fillStyle": "blur",
"categoryOverride": "interview"
}
},
"userMetadata": {
"customerJobRef": "s3_direct-my-media-bucket-clips-demo"
}
}S3 direct — source resolved from transcript (only textSourceId required; optional outputPrefix and client options):{
"serviceType": "clip_generation",
"input": {
"sourceType": "s3_direct",
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"options": {
"maxClips": 5,
"layout": "horizontal_in_vertical",
"fillStyle": "blur"
}
},
"userMetadata": {
"customerJobRef": "clips-from-txn-demo"
}
}Completed clips job:{
"jobId": "job_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"serviceType": "clip_generation",
"status": "completed",
"workspaceId": "wsp_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"projectId": "prj_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"resultSummary": {
"clipGenerationId": "clp_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"clipCount": 3,
"sourceDurationSec": 842.5,
"transcriptPreview": "Welcome back to the channel…"
},
"input": {
"sourceType": "s3_direct",
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"options": {
"maxClips": 3,
"layout": "vertical_9_16",
"fillStyle": "blur",
"analysis": { "vision": true, "category": true }
}
},
"createdAt": "2026-05-18T12:34:56Z",
"startedAt": "2026-05-18T12:34:57Z",
"completedAt": "2026-05-18T12:45:42Z"
}Stored job input.options includes resolved client options (maxClips, layout, fillStyle, optional categoryOverride) plus platform-managed analysis.vision and analysis.category (read-only on the job object). Disabled analysis stages appear as skipped in progress[]. The completed ClipGeneration record includes layout, category, and categoryConfidence.
Clips credits are charged when the job is accepted — 1 credit per input.options.maxClips. Failed jobs after queueing still count. Free plan default: 1,000 clip credits per billing period (see Plans & limits).
Transcription reference validation (at create)
- The API checks that each ID exists in the current project (API key / console scope) and has at least one stored segment before the job is queued. Invalid references are rejected synchronously — no credits are consumed.
- 404
notFound— unknown ID or transcript in another project. Message:transcription not found: …(clip/thumbnail may also returntranscription job not found: job_…if the linked job row is missing). - 422
validationError— transcript exists but has no segments (segmentCountis 0 and no segment payload). Message:transcription has no segments. - 422
validationError— source transcription job exists butstatusis notcompleted(issue: jobNotCompletedoninput.textSourceId). error.details[]includespathandissuefor integration fixes. See Errors.
Example (not found):
{
"error": {
"code": "notFound",
"message": "transcription not found: txt_01HX…",
"details": [ { "path": "input.textSourceId", "issue": "notFound:txt_01HX…" } ]
}
}Example (no segments):
{
"error": {
"code": "validationError",
"message": "transcription has no segments",
"details": [ { "path": "input.textSourceId", "issue": "emptySegments" } ]
}
}Parameters
- serviceTypestring
- Must be
clip_generation. - input.sourceTypestring, optional
- Must be
s3_directwhen set explicitly. - input.connectionIdstring, optional
- Storage connection id (<DocCode>s3_direct</DocCode> only).
- input.bucketstring, optional
- Bucket on the connection (<DocCode>s3_direct</DocCode> only).
- input.sourceKeystring, optional
- Object key for the source video (<DocCode>s3_direct</DocCode> only).
- input.outputPrefixstring, optional
- Optional output location for clip MP4s (
s3_direct; API field name:outputPrefix). When omitted, outputs are written underperchat-media-results/{transcriptionId}/clips/(or{defaultOutputPrefix}/{transcriptionId}/clips/when the bucket'sdefaultOutputPrefixis set on the storage connection). Files are placed directly in that folder (e.g.full_text.txt,clip_01.mp4,thumbnail_1.jpg). SetoutputPrefixto override with a custom folder path — it is not combined again withdefaultOutputPrefix. - input.textSourceIdstring
- Required completed transcription id (
txt_…) in this project, with segments. Rejected at create withnotFoundorvalidationErrorif invalid (see validation notes on this endpoint). The worker loads stored segments from this text source — Whisper is not run when generating clips. Fors3_direct, source connection fields can be omitted; they are resolved from the completed transcription job. - input.options.maxClipsinteger, optional
- Clips to generate. Defaults to
maxClipsDefaultand is capped atmaxClipsCapfor the workspace plan (live values:GET /v1/jobs/policy?serviceType=clip_generation). Consumes the same number of clip credits at job create (1 credit per clip). Do not sendinput.options.analysis— vision and category detection are platform-managed (see notes). - input.options.layoutstring, optional
- Output layout for all clips in this job. One of
vertical_9_16(short-form vertical),horizontal_in_vertical(landscape in vertical frame),horizontal_16_9(widescreen), orsquare_1_1(square). Omit to use the platform default or auto-select from detected content category. Must appear insupportedLayoutsfor your plan (GET /v1/jobs/policy?serviceType=clip_generation). - input.options.fillStylestring, optional
- Letterbox fill for
horizontal_in_verticallayout:black,blur, orbranding. Defaults to the platform render default. Must appear insupportedFillStylesfor your plan. - input.options.categoryOverridestring, optional
- Force content category instead of auto-detection (for example
interview,podcast,gaming). Allowed values are listed insupportedCategorieswhenallowCategoryOverrideistrueon your plan. Skips thedetect_categoryprogress stage when set. - userMetadata.customerJobRefstring, optional
- Stable reference for filtering jobs (max 120 characters).
Returns
status: queued. When complete, resultSummary includes clipGenerationId and clipCount; fetch full clip metadata (including resolved layout, category, and per-clip spans) via GET /v1/clip-generations/{clipGenerationId}.See the the job object for attribute details.
Create a thumbnail job
/v1/jobsinput.options.thumbnailCount must be 1–3). The worker reads transcript segments from input.textSourceId, uploads JPEG images to your S3 bucket, and stores metadata on the transcription. Use Thumbnails or GET /v1/transcriptions/{id} to fetch results after the job completes.When to use
- Prerequisite — a completed transcription job with segments; pass its
transcriptionId. Source video location is resolved from that job when omitted ons3_direct. - S3 storage connection — use a saved storage connection; the API resolves thumbnail output keys under
{defaultOutputPrefix}/{transcriptionId}/thumbnails/unlessoutputPrefixis set.
Requires permission
job:create
Each thumbnail job generates at most 3 JPEG variants. Set input.options.thumbnailCount to 1, 2, or 3 — values above 3 are rejected at validation. Outputs are written to your bucket via the job's storage connection.
Complete a transcription job first and pass its textSourceId — thumbnail jobs reuse stored segments and never run Whisper. Do not send pipeline tuning fields — imageModel, imageQuality (low, medium, high, or auto for gpt-image-1), frame limits, and vision model are configured in platform admin (Jobs → Thumbnail) and injected at job creation. Clients may set input.options.thumbnailCount (1–3), nonAiThumbnailCount, and the output size via ratioWidth / ratioHeight / resolution.
{
"serviceType": "thumbnail",
"input": {
"sourceType": "s3_direct",
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"connectionId": "e7b2c4d8-0000-4000-a000-000000000001",
"bucket": "my-media-bucket",
"sourceKey": "media/videos/product-demo.mp4",
"options": {
"thumbnailCount": 3
}
}
}S3 direct — source resolved from transcript (only textSourceId required; optional outputPrefix):{
"serviceType": "thumbnail",
"input": {
"sourceType": "s3_direct",
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"connectionId": "e7b2c4d8-0000-4000-a000-000000000001",
"bucket": "my-media-bucket",
"sourceKey": "media/videos/product-demo.mp4",
"options": {
"thumbnailCount": 3
}
}
}S3 direct — source resolved from transcript (only transcriptionId required; optional outputPrefix):{
"serviceType": "thumbnail",
"input": {
"sourceType": "s3_direct",
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"options": {
"thumbnailCount": 2
}
}
}Completed thumbnail job:{
"jobId": "job_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"serviceType": "thumbnail",
"status": "completed",
"resultSummary": {
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"thumbnailCount": 2,
"newThumbnailCount": 2,
"nonAiThumbnailCount": 1,
"generated": true
},
"input": {
"sourceType": "s3_direct",
"textSourceId": "txt_01HX2K9V3JEK4WYZN6F0XQ2W8M"
},
"createdAt": "2026-05-29T10:00:00Z",
"startedAt": "2026-05-29T10:00:01Z",
"completedAt": "2026-05-29T10:03:06Z"
}Stored job input.options reflects platform thumbnail settings at create time (read-only on the job object), including imageQuality (low | medium | high | auto for gpt-image-1).
Thumbnail credits are charged when the job is accepted — 1 credit per input.options.thumbnailCount (maximum 3 per job). Failed jobs after queueing still count. Free plan default: 100 thumbnail credits per billing period (see Plans & limits).
Transcription reference validation (at create)
- The API checks that each ID exists in the current project (API key / console scope) and has at least one stored segment before the job is queued. Invalid references are rejected synchronously — no credits are consumed.
- 404
notFound— unknown ID or transcript in another project. Message:transcription not found: …(clip/thumbnail may also returntranscription job not found: job_…if the linked job row is missing). - 422
validationError— transcript exists but has no segments (segmentCountis 0 and no segment payload). Message:transcription has no segments. - 422
validationError— source transcription job exists butstatusis notcompleted(issue: jobNotCompletedoninput.textSourceId). error.details[]includespathandissuefor integration fixes. See Errors.
Example (not found):
{
"error": {
"code": "notFound",
"message": "transcription not found: txt_01HX…",
"details": [ { "path": "input.textSourceId", "issue": "notFound:txt_01HX…" } ]
}
}Example (no segments):
{
"error": {
"code": "validationError",
"message": "transcription has no segments",
"details": [ { "path": "input.textSourceId", "issue": "emptySegments" } ]
}
}Parameters
- serviceTypestring
- Must be
thumbnail. - input.transcriptionIdstring
- Completed transcription to analyze (
txt_…) in this project, with segments. Required. Rejected at create if the ID is unknown or has no segments (see validation notes on this endpoint). Source video fields are resolved from the completed transcription job when not supplied. - input.options.thumbnailCountinteger, optional
- Number of AI thumbnail variants to generate (1–3, maximum 3 per job). Values > 3 fail validation. Defaults to the platform
defaultThumbnailCountfor the workspace plan (also capped at 3). One credit per variant. - input.options.nonAiThumbnailCountinteger, optional
- Number of non-AI (raw frame) thumbnails to generate (1–5). Defaults to the platform
maxNonAiThumbnailCountfor the workspace plan. Fors3_presignedjobs, provide a matching number of presigned PUT URLs in theoutputUploadUrls.nonAiarray. - input.options.ratioWidthinteger, optional
- Output aspect-ratio width — paired with
ratioHeight(e.g. 16:9). The image model is set by your plan: free usesgpt-image-1(only1:1,3:2, or2:3atresolution1024), enterprise usesgpt-image-2(any ratio up to 3:1). Send all three ofratioWidth,ratioHeight,resolutiontogether, or omit them to use the platform default. Invalid combinations are rejected at job creation. - input.options.ratioHeightinteger, optional
- Output aspect-ratio height — paired with
ratioWidth. SeeratioWidthfor the per-plan rules. - input.options.resolutioninteger, optional
- Output resolution as the shorter edge in pixels (e.g. 720, 1080, 1440, 2160). The longer edge is derived from the ratio; both are snapped to a multiple of 16. On gpt-image-2 the resulting
WxHmust have edges ≤ 3840, an aspect ratio ≤ 3:1, and total pixels between 655,360 and 8,294,400 (~0.66–8.29 MP); on gpt-image-1 the only valid value is 1024. Defaults to the platform setting for the plan. - input.sourceTypestring, optional
- Must be
s3_directwhen set explicitly. - input.connectionIdstring, optional
- Storage connection id (
s3_directonly). - input.bucketstring, optional
- Bucket on the connection (<DocCode>s3_direct</DocCode> only).
- input.sourceKeystring, optional
- Object key for the source video (<DocCode>s3_direct</DocCode> only).
- input.outputPrefixstring, optional
- Optional output location for thumbnail JPEGs (
s3_direct; API field:outputPrefix). When omitted, outputs are written underperchat-media-results/{transcriptionId}/thumbnails/(or{defaultOutputPrefix}/{transcriptionId}/thumbnails/when the bucket'sdefaultOutputPrefixis set on the storage connection). Files are placed directly in that folder (e.g.full_text.txt,clip_01.mp4,thumbnail_1.jpg). SetoutputPrefixto override with a custom folder path — it is not combined again withdefaultOutputPrefix.
Returns
status: queued. When complete, resultSummary includes textSourceId, thumbnailCount (accumulated total), newThumbnailCount (added by this run), nonAiThumbnailCount, and generated. Re-running for the same textSourceId adds new, visually different thumbnails to the existing set (it does not replace them). Fetch full image metadata via GET /v1/transcriptions/{transcriptionId} or GET /v1/thumbnails/{transcriptionId}.See the the job object for attribute details.
Retrieve a job
/v1/jobs/{jobId}status, currentStage, retry counters, errors, and — when complete — a service-specific resultSummary (transcription, clips, thumbnail, or metadata). Stage timelines are not included — use GET /v1/jobs/{jobId}/progress from a console session for progress[] and attemptHistory[].When to use
- Polling loop after create — call every few seconds until
statusis terminal (completed,failed, orcancelled). Keep polling whilecancelling. - Lightweight polling — use
statusandcurrentStageuntil terminal. For a full stage timeline, callGET /v1/jobs/{jobId}/progress(console session only). - Job detail page — show attribution (
createdByLabel,apiKeyLabel), retry history, and terminalerror. - Does not return segment text — use
GET /v1/transcriptions/{transcriptionId}onceresultSummary.textSourceIdis set,GET /v1/clip-generations/{clipGenerationId}onceresultSummary.clipGenerationIdis set,GET /v1/thumbnails/{transcriptionId}onceresultSummary.thumbnailCountis set, orGET /v1/transcriptions/{transcriptionId}/metadataonceresultSummary.metadataIdis set.
Requires permission
job:read
duration_exceeded on download):{
"jobId": "job_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"serviceType": "transcription",
"status": "failed",
"priority": "low",
"workspaceId": "wsp_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"projectId": "prj_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"input": {
"sourceType": "s3_direct",
"connectionId": "e7b2c4d8-0000-4000-a000-000000000001",
"bucket": "my-bucket",
"sourceKey": "media/clips/demo.mp4",
"options": {
"language": "en"
}
},
"resultSummary": null,
"currentStage": null,
"attemptCount": 1,
"lastError": {
"at": "2026-05-18T12:34:58Z",
"message": "Video duration 7200.0s exceeds limit 3600s (checked at local_probe)"
},
"error": {
"code": "duration_exceeded",
"message": "Video duration 7200.0s exceeds limit 3600s (checked at local_probe)"
},
"createdAt": "2026-05-18T12:34:56Z",
"updatedAt": "2026-05-18T12:34:58Z",
"startedAt": "2026-05-18T12:34:57Z",
"completedAt": "2026-05-18T12:34:58Z",
"userMetadata": null
}Parameters
- jobIdstring
- Job identifier.
Returns
See the the job object for attribute details.
Retrieve job progress
/v1/jobs/{jobId}/progressprogress[] timeline, currentStage, and attemptHistory[] for one job. Console session only — project API keys receive 403. Use this from the Perchat console or other browser-authenticated tooling; API integrations should poll GET /v1/jobs/{jobId} for status and currentStage instead.When to use
- Job detail timeline in the console — render per-stage events and prior failed attempts.
- Not for API-key polling — unavailable to project keys by design.
Requires permission
job:read
Parameters
- jobIdstring
- Job identifier.
Returns
List all jobs
/v1/jobsWhen to use
- Jobs table / history in a custom dashboard — filter by
statuses, sort bycreated. - Operations monitoring — find stuck
processingjobs or recentfailedruns. - Search by creator (
user), source URL (input), job id substring, or your reference (customerJobRef). - Prefer retrieve when you already have a
jobIdfrom create — list is for browse/search, not polling a single in-flight job.
Requires permission
job:list
Query parameters
- pageinteger, optional
- 1-indexed page. Default 1.
- limitinteger, optional
- Page size. Default 20, max 100.
- jobIdstring, optional
- Filter by job ID substring.
- customerJobRefstring, optional
- Filter by exact userMetadata.customerJobRef value from job creation.
- userstring, optional
- Filter by creator (@username, API key name, or key id substring).
- inputstring, optional
- Filter by source URL or input metadata substring.
- statusesstring, optional
- Comma-separated statuses (e.g. pending,queued,processing,cancelling,cancelled).
- serviceTypesstring, optional
- Comma-separated service types (e.g. transcription,clip_generation,indexer).
- sortstring, optional
- jobId, service, status, user, or created (default).
- sortDirstring, optional
- asc or desc. Default desc.
Returns
data array of Job objects; pagination in metadata.pagination.See the the job object for attribute details.
Cancel a job
/v1/jobs/{jobId}/cancelpending and queued jobs become cancelled immediately. A processing job enters cancelling first; the worker stops after the current pipeline stage, then the job becomes cancelled. Attribution fields record who requested cancellation. Idempotent for cancelling and cancelled.When to use
- User abandons work while
pending,queued, or mid-run. - Cannot cancel
completedorfailedjobs (409conflict). - Graceful stop — a
processingjob becomescancellinguntil the worker finishes the current stage, thencancelled.
Requires permission
job:cancel
pending / queued) returns status: cancelled with completedAt set.Graceful cancel while
processing returns status: cancelling first — poll retrieve until cancelled:{
"jobId": "job_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"serviceType": "transcription",
"status": "cancelling",
"priority": "low",
"workspaceId": "wsp_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"projectId": "prj_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"input": {
"sourceType": "s3_direct",
"connectionId": "e7b2c4d8-0000-4000-a000-000000000001",
"bucket": "my-bucket",
"sourceKey": "media/clips/demo.mp4",
"options": {
"language": "en"
}
},
"resultSummary": null,
"currentStage": "separate_vocals",
"attemptCount": 0,
"lastError": null,
"error": null,
"cancelRequestedAt": "2026-05-18T12:35:05Z",
"cancelRequestedByUserId": "usr_01HX2K9V3JEK4WYZN6F0XQ2W8M",
"cancelRequestedByApiKeyId": null,
"cancelRequestedByLabel": "@vishnu · Console",
"cancelRequestedByKind": "console",
"cancelMode": "graceful",
"createdAt": "2026-05-18T12:34:56Z",
"updatedAt": "2026-05-18T12:35:05Z",
"startedAt": "2026-05-18T12:34:57Z",
"completedAt": null,
"userMetadata": null
}Parameters
- jobIdstring
- Job identifier.