---
title: "Submit one source"
method: POST
path: "/v1/jobs"
tags: ["jobs"]
---

# Submit one source

`POST /v1/jobs`

## Request

- Base URL: `https://api.transcriptdock.com`
- URL: `https://api.transcriptdock.com/v1/jobs`
- Auth: one of:
  - HTTP bearer
  - API key in cookie `sb-access-token`

## Headers

- `Idempotency-Key` string, required

## Request body

- JobRequest: Only source and mode are required. captions_only (1 credit) works for YouTube and TikTok. auto and transcribe run AI transcription (transcribe_credits_per_minute per started minute, 60 s minimum) and need GET /v1/capabilities to report it enabled for the source type; otherwise they return 422 CAPABILITY_UNAVAILABLE. recognition_profile and language only apply to AI transcription. max_credits is an optional ceiling; the charge is transcribe_credits_per_minute on measured duration (see GET /v1/capabilities).
  - `source` union, required
    - object
      - `url` string, uri, required: YouTube or TikTok video URL (captions_only); any https media URL for direct sources (AI transcription). Exactly one of url or upload_id.
      - `upload_id` string, uuid: Id from POST /v1/uploads after the file was PUT and the upload is ready. Exactly one of url or upload_id.
    - object
      - `url` string, uri: YouTube or TikTok video URL (captions_only); any https media URL for direct sources (AI transcription). Exactly one of url or upload_id.
      - `upload_id` string, uuid, required: Id from POST /v1/uploads after the file was PUT and the upload is ready. Exactly one of url or upload_id.
  - `mode` 'captions_only' | 'auto' | 'transcribe', required
  - `language` string, nullable: Only applies to AI transcription (auto/transcribe).
  - `caption_languages` string[]: BCP 47 tags in preference order for the caption track, e.g. ["en", "es"]. Empty means the video default.
  - `caption_preference` 'prefer_creator' | 'creator_only' | 'automatic_only'
  - `recognition_profile` 'standard': Only applies to AI transcription (auto/transcribe).
  - `max_credits` integer: Optional ceiling in credits for auto/transcribe. AI transcription costs transcribe_credits_per_minute per started minute after minimum_billable_seconds (see GET /v1/capabilities); a job whose measured cost exceeds the ceiling fails with BUDGET_EXCEEDED before any provider spend. Default covers max_duration_ms. Ignored for captions_only.
  - `webhook_endpoint_id` string, uuid, nullable: Endpoint from POST /v1/webhooks that receives job.succeeded / job.failed for this job.
  - `metadata` object

## Response `200`

Reused a retained same workspace result for free

- Job
  - `id` string, uuid, required: Job id; use with GET /v1/jobs/{id}.
  - `status` 'queued' | 'processing' | 'awaiting_provider' | 'succeeded' | 'failed' | 'cancelled', required
  - `stage` 'resolve' | 'captions' | 'acquire_audio' | 'submit_asr' | 'wait_asr' | 'finalize' | 'null', nullable, required: Current pipeline step while in flight; null once final.
  - `result_id` string, uuid, nullable, required: Transcript id once status is succeeded; fetch with GET /v1/transcripts/{id}. Null otherwise.
  - `error` ErrorObject, required
    - `code` 'INVALID_REQUEST' | 'INVALID_URL' | 'INVALID_CURSOR' | 'UNSUPPORTED_SOURCE' | 'UNSUPPORTED_MEDIA' | 'CAPABILITY_UNAVAILABLE' | 'UNAUTHENTICATED' | 'FORBIDDEN' | 'EMAIL_UNVERIFIED' | 'NOT_FOUND' | 'INSUFFICIENT_BALANCE' | 'PLAN_REQUIRED' | 'BUDGET_EXCEEDED' | 'IDEMPOTENCY_CONFLICT' | 'JOB_NOT_RETRYABLE' | 'CANCELLATION_NOT_ALLOWED' | 'RATE_LIMITED' | 'SOURCE_NOT_FOUND' | 'SOURCE_PRIVATE' | 'SOURCE_AUTH_REQUIRED' | 'SOURCE_REGION_RESTRICTED' | 'NO_CAPTIONS' | 'LANGUAGE_UNAVAILABLE' | 'LANGUAGE_UNSUPPORTED' | 'NO_SPEECH' | 'NO_AUDIO' | 'TIMESTAMPS_UNAVAILABLE' | 'DURATION_LIMIT_EXCEEDED' | 'FILE_TOO_LARGE' | 'UNSAFE_URL' | 'INVALID_MEDIA' | 'SOURCE_RATE_LIMITED' | 'SOURCE_BLOCKED' | 'SOURCE_TIMEOUT' | 'SOURCE_CHANGED' | 'PROVIDER_UNAVAILABLE' | 'STORAGE_UNAVAILABLE' | 'PROVIDER_REJECTED' | 'INTERNAL_ERROR', required
    - `message` string, required
    - `retryable` boolean, required
    - `retry_after_seconds` integer
    - `details` object
    - `doc_url` string, uri: Where this code is documented.
  - `billing` JobBilling, required
    - `credits_reserved` integer, required: Credits held while the job runs; released or settled when it finishes.
    - `credits_charged` integer, required: Credits charged after settlement (0 until the job finishes). 1 for a caption transcript, transcribe_credits_per_minute per started minute for AI transcription, 0 for a cached result.
    - `kind` 'captions' | 'ai_transcription' | 'cached', required: What the job paid for: captions (1 credit), ai_transcription (per minute), or cached (a reused result, free within the workspace).
  - `source` object
    - `platform` 'youtube' | 'tiktok' | 'instagram' | 'upload' | 'direct', required
    - `media_id` string, nullable, required
    - `canonical_url` string, nullable, required
    - `title` string, nullable, required
    - `upload_id` string, nullable, required
  - `options` object
    - `mode` 'captions_only' | 'auto' | 'transcribe', required
    - `language` string, nullable, required
    - `caption_preference` string, required
  - `created_at` string, date-time, required: Submission time, UTC.

## Other responses

- `202`: Accepted; work queued. Location points at GET /v1/jobs/{id}; Retry-After suggests the first poll delay.
- `402`: Reservation refused (balance, budget, plan)
- `409`: Idempotency key already used with a different body
- `422`: Request shape or capability rejected

---

[API](https://skmtc.dev/transcriptdock/apis/transcriptdock-api.md) · [All operations](https://skmtc.dev/transcriptdock/apis/transcriptdock-api/llms.txt) · [OpenAPI document](https://skmtc.dev/transcriptdock/apis/transcriptdock-api/revisions/7bda7594bdfd?raw)
