Octavia Docs

01 · Introduction

Octavia Documentation

Octavia turns a single video, audio file, or subtitle file into fully localized output — dubbed voice tracks, translated subtitles, lip-synced video, and narrated speech — across 60+ languages. Everything the app does through its UI is reachable through the same API: send a source file, get back a job you can poll or subscribe to with a webhook, and download finished output per language once it renders.

This reference covers both the Octavia app itself and, for teams integrating Octavia into their own product, the exact endpoints, parameters, and payloads for the API. Nothing in the API does anything the app can't already do through its interface — the API just makes it scriptable.

Two ways in: if you just need to dub, subtitle, or narrate something without writing code, skip straight to Using The App below — everything there happens in the browser. If you're integrating Octavia into your own product or pipeline, jump to Developer Quickstart instead, which walks through creating a dub end to end in four requests.

What you can build

  • Automated dubbing pipelines that plug into a CMS, video host, or publishing workflow.
  • Subtitle generation and translation for an existing content library, in bulk.
  • Text-to-speech or subtitle-to-speech narration for voiceovers and audio articles.
  • Webhook-driven systems that pick up finished renders automatically instead of polling.
  • Internal review tools that edit machine translations before they render, using the same transcript endpoints the Octavia app itself uses.

API access is available on the Pro and Studio plans. Studio additionally gets priority queueing for API requests. Free, Starter, and Creator plans can use every workflow through the Octavia app, but need to upgrade to call the API directly. See Pricing for plan details.

Using The App

The dashboard

Everything described in this reference is also available directly through the Octavia app at app.octavia.lunartech.ai — no code required. After signing in, your dashboard lists every dub, subtitle, and speech job in your workspace, each with its current status, target languages, and a shortcut back into the finished file once it's ready.

A credit balance in the top corner tracks what's left in the current billing period (see Billing & plan), and a New Project button starts a fresh job on any of the six workflows covered under Core Workflows: video translation, audio translation, speech generation, subtitle generation, subtitle to audio, and subtitle translation.

Using The App

Create a project

Click New Project and pick a workflow. Each one opens a short setup form: choose or upload a source, pick your target language(s), and decide between Fast and Quality mode depending on whether you're checking a translation or rendering the version you plan to ship. Toggling manual review here does the same thing as passing review: "manual" through the API — it pauses the project after translation so you can proofread before anything renders (see Review & edit).

A project you start in the app and one you create through the API are the same object underneath — a project kicked off from the dashboard shows up if you query it with GET /v1/dubs, and one created through the API shows up on your dashboard just the same.

Using The App

Upload a source

Drag a file in directly, or paste a URL Octavia can fetch on its own. Plan tier sets how long a single source can run: Free caps out at 5 minutes and Starter at 60; Creator, Pro, and Studio raise that ceiling considerably — see Pricing for the exact limit on your plan. Once a source is accepted, the project appears on your dashboard as queued and moves through the same stages described in Job lifecycle regardless of whether it started from the app or the API.

Using The App

Review & edit

If manual review was turned on when the project was created, it pauses after translation with an editable transcript — line by line, per language, with detected speakers labeled where multi-speaker detection applies. Fix a mistranslated name, adjust phrasing, or reassign a line to the correct speaker directly in that screen, then hit Render when you're satisfied. These are the exact same edits available through the transcript endpoints in the API (see Transcript & review) — the app screen and the API are two views onto the same in-review project, so edits made in one show up in the other immediately.

Using The App

Export & download

Once a project reaches complete, every target language is downloadable from its detail page, individually or as a batch. Video dubs export as the original video with the dubbed, lip-synced audio track baked in; audio and speech jobs export as an audio file; subtitle jobs export as SRT or VTT. Starter and above export without a watermark; Free-tier exports carry one.

Using The App

Workspace & team

A workspace is shared by everyone on your plan's seat allowance — Free, Starter, and Creator include a single seat, Pro includes two, and Studio includes five (see Pricing for the full breakdown, including how many jobs can run concurrently per plan). Invite teammates and manage roles from Settings → Team. Everyone in a workspace shares the same credit balance and project history, so a teammate's finished dub shows up on your dashboard too, not just theirs.

Using The App

Billing & plan

Plans, credits, and payment details live under Settings → Billing, handled through Stripe. Billing is monthly with no long-term contract. Upgrades apply immediately, so you get the new plan's credits and limits right away; downgrades take effect at your next renewal, so you don't lose access mid-cycle. Cancel any time from the same screen — there's no cancellation fee.

Developer Quickstart

Quickstart

Everything past this point is for teams integrating Octavia into their own product or pipeline rather than using the app directly.

Base URL

All API requests are made against:

Base URL
https://api.octavia.ai/v1

v1 is the current stable API version and the one every example on this page uses. New fields and endpoints are added in a backward-compatible way; nothing existing is removed or renamed without a new version prefix. If you need to track exactly what changed and when, see the Changelog.

SDKs

There's an official JavaScript/TypeScript client, OctaviaClient, that wraps the REST API with typed methods:

Node / TypeScript
// npm install @octavia/sdk
import { OctaviaClient } from '@octavia/sdk';

const octavia = new OctaviaClient({
  apiKey: 'oct_sk_...'
});

// Dub your video content
const results = await octavia.dub.translate({
  videoUrl: 'https://example.com/video.mp4',
  targetLanguages: ['es', 'fr', 'de']
});

console.log(results.dubbedVideos);

If you're not on Node, every endpoint is plain REST and a GraphQL endpoint is also available for clients that want to fetch nested resources (a dub with its transcript lines and output URLs) in a single round trip. This reference documents the REST surface; the GraphQL schema mirrors the same resources.

Create your first dub

This walks through dubbing a video into two languages using nothing but curl. It's the same sequence the OctaviaClient SDK performs under the hood.

1 · Get an API key

Create one from Settings → API Keys in the Octavia app. Keys are prefixed oct_sk_ and shown once — store it somewhere safe.

2 · Set it as an environment variable

Shell
export OCTAVIA_API_KEY="oct_sk_live_..."

3 · Create a dub

POST /v1/dubs
curl https://api.octavia.ai/v1/dubs \
  -H "Authorization: Bearer $OCTAVIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "videoUrl": "https://example.com/video.mp4",
    "targetLanguages": ["es", "fr"]
  }'

The response returns immediately with a queued job and its id:

Response · 201 Created
{
  "id": "dub_31xk9m",
  "status": "queued",
  "targetLanguages": ["es", "fr"],
  "createdAt": "2026-08-16T09:12:03Z"
}

4 · Check status, then download each finished language

GET /v1/dubs/{id}
curl https://api.octavia.ai/v1/dubs/dub_31xk9m \
  -H "Authorization: Bearer $OCTAVIA_API_KEY"
GET /v1/dubs/{id}/output/{lang}
curl -L https://api.octavia.ai/v1/dubs/dub_31xk9m/output/es \
  -H "Authorization: Bearer $OCTAVIA_API_KEY" \
  -o dubbed-es.mp4

By default a dub renders immediately. Pass "review": "manual" in the create request to pause after translation, edit any line through the API, and render only once you approve — see Transcript & review. Rather than polling, you can also register a webhook and get notified the moment each language finishes.

Developer Quickstart

Authentication

Every request is authenticated with a bearer token in the Authorization header:

Header
Authorization: Bearer oct_sk_live_...

API keys are created and revoked from Settings → API Keys and are scoped to a single workspace. A few rules worth building around:

  • Keys are shown once at creation — if you lose one, revoke it and issue a new one rather than trying to recover it.
  • Never embed a key in client-side code, a mobile app bundle, or a public repository. Call Octavia from your own backend, or use a short-lived, narrowly-scoped key if a request has to originate from a browser.
  • Use separate keys per environment (development, staging, production) so a compromised staging key can't touch production jobs, and so usage per environment shows up separately in usage reporting.
  • Keys can be created with restricted permission scopes — for example a key that can create dubs but can't read usage or manage webhooks.

Requests without a valid Authorization header return 401 Unauthorized. Requests from a plan without API access return 403 Forbidden.

Developer Quickstart

Errors & limits

Octavia uses conventional HTTP status codes: 2xx for success, 4xx when the request itself needs fixing, 5xx when something failed on our end and is usually safe to retry.

HTTP status codes
StatusMeaning
400The request body is malformed — invalid JSON, missing a required field.
401Missing, malformed, or revoked API key.
403The key is valid but the plan or scope doesn't allow this action.
404The resource (dub, job, key) doesn't exist or doesn't belong to this workspace.
409The resource is in a state that doesn't allow this action — e.g. rendering a dub that's already rendering.
422The request is well-formed but fails validation; the error body includes the offending field.
429Rate limited — back off and retry using the headers below.
500 / 503Something failed on Octavia's side. Safe to retry with backoff.

Error responses share one shape:

Error shape
{
  "error": {
    "code": "invalid_target_language",
    "message": "'xx' is not a supported language code.",
    "field": "targetLanguages[0]"
  }
}

Rate limits are enforced per API key and scale with plan tier — Studio keys get priority throughput over Pro. Rather than hard-coding a number, read the limit off the response headers on every call:

  • X-RateLimit-Limit — requests allowed in the current window.
  • X-RateLimit-Remaining — requests left in the current window.
  • X-RateLimit-Reset — unix timestamp the window resets at.

On a 429, respect the Retry-After header rather than retrying immediately in a loop.

Core Workflow

Video translation (dubbing)

The core workflow: send a source video, get back the same video dubbed into every target language, lip-synced by default. Under the hood a dub moves through transcription, translation, and rendering — see Job lifecycle for the exact stages.

Create one with POST /v1/dubs, supplying either a videoUrl Octavia can fetch or a direct file upload. sourceLanguage is optional — when omitted, Octavia detects it automatically from the audio. targetLanguages is always an array, even for a single language.

  • Lip-sync is on by default for video sources (lipSync: true) and maps generated speech to mouth movement frame by frame. Set it to false to get a dubbed audio track composited over the original video without resyncing the mouth.
  • Mode (fast or quality) trades render time for fidelity — iterate in fast, switch to quality for the version you ship. See Best practices.
  • Generated speech follows the pacing, tone, and delivery of each source speaker rather than reading in a flat, uniform voice — this is what keeps a dub feeling like the same person talking, not a narrator reading a translation over them.

See the full parameter list under API Reference → Create a dub.

Core Workflow

Audio translation

Audio translation is the same pipeline as video dubbing minus the video and lip-sync stages, so it's a better fit for podcasts, voice memos, lectures, and any audio-only source. Submit audioUrl instead of videoUrl on the same POST /v1/dubs endpoint — Octavia infers the workflow from which field is present. Because there's no video to render or sync, jobs move through rendering faster than a video dub of the same length.

Output is a dubbed audio file per target language, downloadable the same way as a video dub's output.

Core Workflow

Speech generation

Speech generation goes the other direction: instead of dubbing an existing recording, it turns a script you write directly into narration. Use it for voiceovers, IVR prompts, or audio versions of written articles where there's no source audio to translate. POST /v1/speech takes text, a target language, and an optional voiceId — omit the voice and Octavia picks a natural default for that language. speed adjusts pacing without changing pitch.

Speech jobs skip transcription and translation entirely, so they're the fastest workflow in the API — usually rendering in well under the time it takes to read the output back.

Core Workflow

Subtitle generation

POST /v1/subtitles/generate transcribes a video or audio source into timed subtitles — word-level timestamps, punctuation, and speaker labels when more than one voice is detected — without translating or dubbing anything. Useful when you already have the source language covered and just need accurate captions, or as the first step before running subtitle translation separately.

Output is downloadable as SRT or VTT.

Core Workflow

Subtitle to audio

If you already have a subtitle file and want narrated audio without re-transcribing a source recording, submit it directly: POST /v1/dubs with sourceType: "subtitles" and a subtitleUrl instead of a video or audio URL. Octavia uses the subtitle timing as the pacing target for generated speech, then translates into each of targetLanguages as usual. This skips the transcription stage of the job lifecycle since the timed text is already provided.

Core Workflow

Subtitle translation

POST /v1/subtitles/translate translates the text of an existing SRT or VTT file into one or more target languages while leaving the original timing untouched — no audio is generated. It's the lightest-weight localization workflow in the API and the cheapest per minute (see Credits & usage), which makes it a good fit for translating an entire back catalog of captions quickly.

Working With Jobs

How a dub moves

Every dub reports its progress through a small, fixed set of statuses. Poll GET /v1/dubs/{id} to read the current one, or subscribe to webhooks to get pushed each transition instead.

Table 01 · Dub lifecycle
StatusWhat's happening
queuedAccepted and waiting for a worker.
transcribingSpeech-to-text with word timestamps; speakers separated.
translatingEach line translated into every target language.
in_reviewOnly with review=manual: the transcript is editable until you render.
renderingVoice (and lip-sync, for video) generated per language.
completeEvery target language rendered; outputs downloadable.
failedSomething broke; the error rides the status payload.

A job in failed includes an error object with the same shape described in Errors & limits. Failed jobs don't consume credits for the languages that didn't finish rendering.

Working With Jobs

Transcript & review

By default a dub renders as soon as translation finishes. Pass "review": "manual" when creating it, and the job pauses in in_review after translation instead of moving straight to rendering — giving you a window to fix names, terminology, or phrasing before any voice is generated.

While a dub is in_review:

  • Read the full transcript, line by line, per language, with GET /v1/dubs/{id}.
  • Edit an individual line's text, reassign it to a different detected speaker, or nudge its timing with PATCH /v1/dubs/{id}/transcript/{lineId}.
  • When every edit is in, call POST /v1/dubs/{id}/render to resume the job and move it into rendering.

A dub left in_review doesn't expire on any fixed timer, but it also doesn't consume rendering credits until you explicitly render it — a useful way to batch up translation review work without burning budget on drafts you might still change.

Working With Jobs

Multi-speaker detection

On Pro and above, Octavia automatically detects and separates distinct speakers in a source file during transcription, rather than treating the whole track as one voice. Each detected speaker keeps a consistent voice across the entire dub and across every target language, so a conversation between two people in the source stays a conversation between two distinct voices in the translation.

Speaker assignments are visible per transcript line and adjustable during manual review — if two speakers get merged or split incorrectly, reassign the affected lines before rendering rather than living with the mistake in the output.

Languages

Supported languages

Octavia supports 60+ target languages across every workflow, identified by standard language codes (e.g. es, fr, de, ja, ko, hi, ar, pt), with regional variants distinguished where pronunciation or vocabulary meaningfully differs (e.g. pt-BR vs. pt-PT). sourceLanguage is optional on every endpoint that accepts audio or video — when it's omitted, Octavia detects the spoken language automatically before transcribing.

The full, current list of supported codes is machine-readable rather than reproduced here, since it grows over time:

GET /v1/languages
curl https://api.octavia.ai/v1/languages \
  -H "Authorization: Bearer $OCTAVIA_API_KEY"

Platform

Webhooks

Polling GET /v1/dubs/{id} works fine for a one-off script, but for anything running in production, register a webhook instead and let Octavia push each state change to your endpoint the moment it happens.

Register an endpoint with POST /v1/webhooks, or from Settings → Webhooks in the app. Every delivery is a POST with a JSON body:

Webhook payload
{
  "event": "dub.completed",
  "data": {
    "id": "dub_31xk9m",
    "status": "complete",
    "targetLanguages": ["es", "fr"]
  },
  "timestamp": "2026-08-16T09:14:41Z"
}

Each delivery is signed — verify the X-Octavia-Signature header (an HMAC-SHA256 of the raw request body, keyed with your webhook secret) before trusting the payload, the same way you would for any inbound webhook. Reject anything that doesn't match rather than assuming requests to your endpoint can only come from Octavia.

If your endpoint doesn't return a 2xx, the delivery is retried with backoff for a limited number of attempts — treat webhook handling as idempotent so a retried delivery doesn't double-process the same event. See the full list of event types under API Reference → Event types.

Platform

Credits & usage

Credits are the single currency across every Octavia workflow — API usage draws from the same monthly balance as jobs run through the app. Each workflow costs a different amount per minute of source content processed:

Credit cost per minute of source
WorkflowCredits / min
Video translation100
Audio translation80
Subtitle to audio60
Subtitle translation25
Subtitle generation20

Monthly allowance scales with plan: Free includes 500 credits/month, Starter 6,000, Creator 12,000, Pro 30,000, and Studio 120,000 — see Pricing for the full breakdown, including per-plan limits on job length and concurrency. Check your remaining balance at any time with GET /v1/usage rather than tracking it manually; a job that would exceed your remaining balance is rejected with 402 before it starts, so failed credit checks never partially consume a job's allowance.

Platform

Permission scopes

API access itself is gated by plan — Pro and Studio only, with Studio getting priority throughput. Within an eligible workspace, individual keys can be scoped down further so an integration only has the access it actually needs:

Available scopes
ScopeGrants
dubs:readRead dub status, transcripts, and output URLs.
dubs:writeCreate, edit, render, and cancel dubs.
subtitles:readRead generated or translated subtitle jobs.
subtitles:writeCreate subtitle generation and translation jobs.
speech:writeCreate speech generation jobs.
webhooks:manageCreate, update, and delete webhook endpoints.
usage:readRead credit balance and usage history.

A key created for a one-way ingest pipeline, for example, typically only needs dubs:write and webhooks:manage — it never needs to read usage or manage other keys. Scope keys to the minimum they need.

Best Practices

Building reliable integrations

  • Prefer webhooks over polling. If you must poll, don't do it more than once every 5–10 seconds per job — rendering a video dub reliably takes longer than a single poll interval anyway.
  • Always send targetLanguages as an array, even for a single language. It keeps the response shape (and your parsing code) identical regardless of how many languages you request.
  • Use review=manual for anything long-form — lectures, interviews, podcasts — where names, jargon, and brand terms are easy for machine translation to get wrong. Fixing a line before render is free; re-rendering after the fact isn't.
  • Pass an idempotencyKey on POST /v1/dubs when your client might retry a request after a network failure. Retrying with the same key returns the original job instead of creating a duplicate one.
  • Store the returned id immediately. It's what you'll use to poll, to correlate incoming webhook events, and to fetch output — treat it as the join key between your system and Octavia's.
  • Iterate in fast mode, ship in quality mode. Fast mode is meant for checking translation and pacing before you commit credits to a final render.
  • Verify webhook signatures and make your handler idempotent — see Webhooks.

Heads up: generated speech follows the tone and pacing of the source speaker but Octavia doesn't offer a standalone voice-cloning feature today — you can't submit a separate voice sample and have it applied independently of a source recording. If your workflow assumes that capability, check back or reach out via Contact before building around it.