Automate Tella to YouTube Uploads With a Review Step
Export Tella videos, stop on failed exports, upload privately to YouTube, and review before public publishing.
TellaClaude CodePublishing a Tella recording to YouTube involves several separate operations. You need to export the edited video, wait for the export, download the file, upload it, and review the result.
This guide shows a practical workflow for those steps. It uses Tella's current Public API and YouTube Data API v3. It keeps public publishing behind a private review.
Prerequisites: Familiarity with Tella MCP is helpful but not required. See Part 1: Getting Started with Tella MCP for background.
The workflow
The workflow has one approval boundary:
Tella video -> start export -> wait for completion -> download file
-> upload to YouTube as private -> review -> publish
Tella and YouTube use different authentication systems. Tella uses an API key. YouTube uses OAuth 2.0 because the upload belongs to a channel.
A reliable integration stores the Tella exportId with the resulting YouTube video ID. That record helps prevent duplicate uploads when a request retries.
What you need
- A Tella account with Public API access
- A YouTube channel
- A Google Cloud project with YouTube Data API v3 enabled
- Node.js and a server-side runtime
- A secure place for API keys and OAuth credentials
The Tella API uses Bearer authentication. Create an API key in Settings > API Keys. Tella shows the key only when you create it. Store it in an environment variable.
Configure TELLA_TOKEN through your runtime's environment or secret settings. Avoid typing the key directly into a shell command.
Do not place this token in browser code. Do not commit it to version control. Tella's authentication guide documents the header and key lifecycle.
For YouTube, create OAuth 2.0 credentials in Google Cloud. Configure the consent screen for your use case. Request the narrow upload scope:
https://www.googleapis.com/auth/youtube.upload
Use Google's OAuth guide for the flow that matches your runtime. A desktop script and a web service need different redirect handling. Keep the client credential and refresh credentials on the server.
Install a maintained YouTube client library for your language. The examples below use the Node.js googleapis client. The snippets show the API shape. They are not a complete production script.
npm install googleapis
Find the Tella video
You can list videos with GET /v1/videos. You can retrieve one video with GET /v1/videos/{id}. Use the returned ID when starting an export.
const TELLA_BASE = 'https://api.tella.com/v1';
const tellaHeaders = {
Authorization: `Bearer ${process.env.TELLA_TOKEN}`
};
const response = await fetch(`${TELLA_BASE}/videos/${encodeURIComponent(videoId)}`, {
headers: tellaHeaders
});
if (!response.ok) {
throw new Error(`Tella video lookup failed: ${response.status}`);
}
const video = await response.json();
The Tella quickstart shows the list and detail requests. Check that the video is the intended revision before exporting it.
Start a Tella export
Tella exports are asynchronous. A successful request returns an export object. Save its exportId.
The current endpoint is POST /v1/videos/{id}/exports. The request can set resolution, frame rate, playback speed, subtitles, and granularity.
const exportResponse = await fetch(
`${TELLA_BASE}/videos/${encodeURIComponent(videoId)}/exports`,
{
method: 'POST',
headers: {
...tellaHeaders,
'Content-Type': 'application/json'
},
body: JSON.stringify({
fps: '30',
granularity: 'video',
resolution: '1080p',
speed: '1',
subtitles: false
})
}
);
if (!exportResponse.ok) {
throw new Error(`Tella export request failed: ${exportResponse.status}`);
}
const { export: exportJob } = await exportResponse.json();
console.log(exportJob.exportId, exportJob.status);
This request uses the documented video granularity. Choose another value only when you need clips, tracks, or raw recordings. The start export reference lists the accepted values.
Wait for the export
You have two choices. Polling asks Tella for the export status. A webhook lets Tella notify your endpoint.
Polling
Polling works well for a small script. Choose an interval and a maximum number of attempts. The interval is an implementation choice. It is not an official Tella processing time.
async function waitForExport(videoId, exportId, options = {}) {
const intervalMs = options.intervalMs ?? 30000;
const maxAttempts = options.maxAttempts ?? 120;
const encodedExportId = encodeURIComponent(exportId);
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
const response = await fetch(
`${TELLA_BASE}/videos/${encodeURIComponent(videoId)}/exports/${encodedExportId}`,
{ headers: tellaHeaders }
);
if (!response.ok) {
throw new Error(`Tella export status failed: ${response.status}`);
}
const { export: currentExport } = await response.json();
if (currentExport.status === 'failed') {
throw new Error('Tella reported a failed export.');
}
if (currentExport.status === 'completed' && currentExport.downloadUrl) {
return currentExport.downloadUrl;
}
await new Promise((resolve) => setTimeout(resolve, intervalMs));
}
throw new Error('The export did not complete within the polling limit.');
}
The export status endpoint returns progress and a download URL when complete. Tella export IDs can contain slashes. Encode the ID as one path segment. The status reference calls this out.
Stop when the export reports failed. Keep a maximum wait for queued or running exports. A permanent loop hides failures and complicates operations. Log the video ID, export ID, status, and last error. Do not log the API key.
Webhooks
Webhooks remove repeated status requests. Tella sends an HTTP POST when the export is ready.
Create an endpoint and subscribe to export.ready:
curl --request POST https://api.tella.com/v1/webhooks/endpoints \
--header "Authorization: Bearer $TELLA_TOKEN" \
--header 'Content-Type: application/json' \
--data '{
"url": "https://example.com/webhooks/tella",
"filterTypes": ["export.ready"]
}'
Tella returns an endpoint ID and a signing secret. The secret is shown only when the endpoint is created. Store it securely.
The documented event has this shape:
{
"type": "export.ready",
"data": {
"downloadUrl": "https://cdn.tella.tv/exports/example.mp4",
"exportId": "exp_abc123",
"timestamp": "2024-01-15T10:30:00Z",
"videoId": "vid_xyz789",
"videoName": "Product Demo"
}
}
Verify the svix-id, svix-timestamp, and svix-signature headers before processing. Use the raw request body for signature verification. Tella recommends Svix verification libraries.
Return a 2xx response within 30 seconds. Queue the download and YouTube work after verification. Tella retries failed or timed-out deliveries. Store the event ID and exportId for deduplication. Read the Tella webhook guide for delivery and signature details.
Do not treat a webhook as proof that a video is public. It only indicates that a Tella export is ready.
Which option fits?
Use polling for a local command or a low-volume process. Use webhooks when a service already has an HTTPS endpoint and a queue.
Tella currently documents a limit of 100 requests per minute per user within a workspace. API keys and external MCP connections share that limit. A 429 response includes Retry-After. Respect that value before retrying. See Tella rate limiting.
Download the file
Download the file only after Tella reports status: completed and provides downloadUrl. Check the response before opening a file stream.
const fileResponse = await fetch(downloadUrl);
if (!fileResponse.ok || !fileResponse.body) {
throw new Error(`Tella download failed: ${fileResponse.status}`);
}
// Stream the body to private storage or a temporary file.
The API documentation does not state a fixed lifetime for downloadUrl. Treat it as a delivery URL. Download it during the same workflow and keep your own copy when you need retention.
The file is an input to YouTube. Validate its size, MIME type, and local path before upload. Remove temporary files after a successful upload and after you record the YouTube ID.
Upload privately to YouTube
Complete Google's authorization flow before running this step. Confirm that it authorizes the intended channel.
This example assumes an authorized OAuth client and a downloaded local file. Supply authorizedOAuthClient and localVideoPath from those earlier steps.
const { google } = require('googleapis');
const fileSystem = require('node:fs');
const youtube = google.youtube({
version: 'v3',
auth: authorizedOAuthClient
});
const { data: uploadedVideo } = await youtube.videos.insert({
part: ['snippet', 'status'],
notifySubscribers: false,
requestBody: {
snippet: {
title: 'My Tella walkthrough',
description: 'A walkthrough recorded in Tella.',
categoryId: '22'
},
status: { privacyStatus: 'private' }
},
media: { body: fileSystem.createReadStream(localVideoPath) }
});
if (!uploadedVideo.id) {
throw new Error('YouTube returned no video ID.');
}
console.log('Private upload created:', uploadedVideo.id);
The videos.insert reference documents upload fields and accepted media types. The official Node.js client explains authorization and media streams.
Choose the category that fits your video before uploading. The example uses category 22.
Record the returned video ID against the export job. A successful upload response does not mean video processing has finished.
Review and publish
Open the uploaded video in YouTube Studio. Wait for processing and review the picture, audio, title, description, audience settings, and visibility.
Once it is ready, publish it through Studio. Keeping this last step manual gives you a straightforward review point. You can automate it later when the review process is established.
Google restricts API uploads from some unverified projects to private viewing. Those projects need an audit before that restriction can be lifted. Check the notice in the upload reference before planning public API publishing.
Handle failures without duplicate uploads
Record each stage separately: export requested, export ready, download complete, upload started, and YouTube ID recorded.
If a request times out during upload, check the channel before starting a new upload. The upload may have succeeded even when your script missed the response.
For larger files, consider Google's resumable upload protocol. Store the upload session so the workflow can check progress after an interruption.
Check your project's available quota before processing a batch. Google documents upload allowances and other request costs in its quota guide.
Avoid promising a fixed number of uploads based on an old quota estimate. Project allocations and API rules can change.
Measure the result
Time one manual upload and one automated upload using comparable videos. Record hands-on work separately from export and upload waiting time.
Include setup, review, and failed attempts when deciding whether automation helps. Your publishing volume and connection speed affect the result.
Next step
Start with one short Tella recording. Export it, upload it privately, and confirm that your workflow records the YouTube ID.
Review that upload in Studio before adding more videos. Once the single-video path works, add bounded retries and a queue for larger batches.
For related workflows, see transcript-to-blog drafts and short clips with Tella.
Keep learning
More guides, free videos and course access for existing members, all in one place.
Go to LearnRather have it built for you?
We'll match you with vetted agentic talent who build it with AI, inside the tools you already use.
Schedule a callRelated posts

Use the Tella Video API for Library Sync and Blog Drafts
Use Tella API pagination and playlist events for a library sync or reviewed blog draft, with duplicate and readiness checks.

Circle AI Agents Review: Automate Community Management with AI
Read my Circle AI Agent tests, current plan notes, and a support-demand method for evaluating cost and answer quality.
Build Custom AI Agents for Other Businesses
Scope a custom business agent around one workflow, approved sources, read-only testing, and human review.