Origins
Origins are storage locations where your source videos live and where transcoded outputs are written. Transcodely supports four storage providers: Google Cloud Storage (GCS), Amazon S3, Cloudflare R2, and HTTP/HTTPS endpoints.
Overview
Every transcoding job needs at least one origin — an input origin to read the source video, and an output origin to write the transcoded files. Origins store the credentials and configuration needed to access your storage buckets.
Job Request
├── Input Origin (read) — where the source video is
└── Output Origin (write) — where outputs are writtenProviders
| Provider | Protocol | Permissions | Use Case |
|---|---|---|---|
| GCS | gs:// | Read + Write | Google Cloud users |
| S3 | s3:// | Read + Write | AWS or S3-compatible storage |
| R2 | s3:// | Read + Write | Cloudflare R2 — zero egress fees |
| HTTP | https:// | Read only | CDNs, public URLs, third-party APIs |
A fifth provider, Transcodely-managed, is created automatically when hosting is provisioned. Managed origins are read-only and system-created: they appear in List and Get responses (flagged with is_managed) but cannot be created, modified, or archived directly.
Creating a GCS Origin
Point Transcodely at a Google Cloud Storage bucket by supplying a gcs block with the bucket name and a service account key. Grant read, write, or both via permissions, and optionally set a base_path and path_template to control where outputs are written.
curl -X POST https://api.transcodely.com/transcodely.v1.OriginService/Create
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{
"name": "Production GCS Bucket",
"description": "Main storage for video assets",
"permissions": ["read", "write"],
"base_path": "videos/",
"path_template": "{date}/{job_id}/{codec}_{resolution}",
"gcs": {
"bucket": "acme-video-assets",
"credentials": {
"service_account_json": "{...service account key JSON...}"
}
}
}'const origin = await client.origins.create({
name: "Production GCS Bucket",
description: "Main storage for video assets",
permissions: [OriginPermission.READ, OriginPermission.WRITE],
basePath: "videos/",
pathTemplate: "{date}/{job_id}/{codec}_{resolution}",
gcs: {
bucket: "acme-video-assets",
credentials: {
serviceAccountJson: "{...service account key JSON...}",
},
},
});origin = client.origins.create(
name="Production GCS Bucket",
description="Main storage for video assets",
permissions=["read", "write"],
base_path="videos/",
path_template="{date}/{job_id}/{codec}_{resolution}",
gcs={
"bucket": "acme-video-assets",
"credentials": {
"service_account_json": "{...service account key JSON...}",
},
},
)origin, err := client.Origins.Create(ctx, &transcodely.OriginCreateParams{
Name: "Production GCS Bucket",
Description: "Main storage for video assets",
Permissions: []transcodely.OriginPermission{transcodely.OriginPermissionRead, transcodely.OriginPermissionWrite},
BasePath: "videos/",
PathTemplate: "{date}/{job_id}/{codec}_{resolution}",
Gcs: &transcodely.GcsOriginConfig{
Bucket: "acme-video-assets",
Credentials: &transcodely.GcsCredentials{
ServiceAccountJson: "{...service account key JSON...}",
},
},
})Credentials are validated at creation time. If validation fails, the origin is created with a failed status and the validation error is returned.
Creating an S3 Origin
Amazon S3 origins take an s3 block with the bucket, its region, and an access key ID / secret access key pair. As with any origin, set permissions to read, write, or both.
curl -X POST https://api.transcodely.com/transcodely.v1.OriginService/Create
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{
"name": "AWS S3 Output",
"permissions": ["read", "write"],
"s3": {
"bucket": "acme-transcoded-outputs",
"region": "us-east-1",
"credentials": {
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
}'const origin = await client.origins.create({
name: "AWS S3 Output",
permissions: [OriginPermission.READ, OriginPermission.WRITE],
s3: {
bucket: "acme-transcoded-outputs",
region: "us-east-1",
credentials: {
accessKeyId: "AKIAIOSFODNN7EXAMPLE",
secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
},
},
});origin = client.origins.create(
name="AWS S3 Output",
permissions=["read", "write"],
s3={
"bucket": "acme-transcoded-outputs",
"region": "us-east-1",
"credentials": {
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
},
},
)origin, err := client.Origins.Create(ctx, &transcodely.OriginCreateParams{
Name: "AWS S3 Output",
Permissions: []transcodely.OriginPermission{transcodely.OriginPermissionRead, transcodely.OriginPermissionWrite},
S3: &transcodely.S3OriginConfig{
Bucket: "acme-transcoded-outputs",
Region: "us-east-1",
Credentials: &transcodely.S3Credentials{
AccessKeyId: "AKIAIOSFODNN7EXAMPLE",
SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
},
},
})S3-compatible storage
The s3 provider also works with any S3-compatible object store — Hetzner Object Storage, Wasabi, DigitalOcean Spaces, MinIO, Backblaze B2, and others. Add an endpoint and Transcodely uses path-style addressing against that host instead of AWS. The region is still required for request signing (for Hetzner it’s the location code, e.g. fsn1).
curl -X POST https://api.transcodely.com/transcodely.v1.OriginService/Create
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{
"name": "Hetzner Object Storage",
"permissions": ["read", "write"],
"s3": {
"bucket": "acme-video-inputs",
"region": "fsn1",
"endpoint": "https://fsn1.your-objectstorage.com",
"credentials": {
"access_key_id": "YOUR_HETZNER_ACCESS_KEY",
"secret_access_key": "YOUR_HETZNER_SECRET_KEY"
}
}
}'const origin = await client.origins.create({
name: "Hetzner Object Storage",
permissions: [OriginPermission.READ, OriginPermission.WRITE],
s3: {
bucket: "acme-video-inputs",
region: "fsn1",
endpoint: "https://fsn1.your-objectstorage.com",
credentials: {
accessKeyId: "YOUR_HETZNER_ACCESS_KEY",
secretAccessKey: "YOUR_HETZNER_SECRET_KEY",
},
},
});origin = client.origins.create(
name="Hetzner Object Storage",
permissions=["read", "write"],
s3={
"bucket": "acme-video-inputs",
"region": "fsn1",
"endpoint": "https://fsn1.your-objectstorage.com",
"credentials": {
"access_key_id": "YOUR_HETZNER_ACCESS_KEY",
"secret_access_key": "YOUR_HETZNER_SECRET_KEY",
},
},
)origin, err := client.Origins.Create(ctx, &transcodely.OriginCreateParams{
Name: "Hetzner Object Storage",
Permissions: []transcodely.OriginPermission{transcodely.OriginPermissionRead, transcodely.OriginPermissionWrite},
S3: &transcodely.S3OriginConfig{
Bucket: "acme-video-inputs",
Region: "fsn1",
Endpoint: proto.String("https://fsn1.your-objectstorage.com"),
Credentials: &transcodely.S3Credentials{
AccessKeyId: "YOUR_HETZNER_ACCESS_KEY",
SecretAccessKey: "YOUR_HETZNER_SECRET_KEY",
},
},
})Creating a Cloudflare R2 Origin
R2 supports two configuration styles. The recommended path supplies your Cloudflare account_id and lets us derive the endpoint server-side. An optional jurisdiction controls data residency.
curl -X POST https://api.transcodely.com/transcodely.v1.OriginService/Create
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{
"name": "Cloudflare R2 Outputs",
"permissions": ["read", "write"],
"r2": {
"bucket": "acme-transcoded-outputs",
"account_id": "f037e1abcd0987654321fedcba012345",
"credentials": {
"access_key_id": "...",
"secret_access_key": "..."
}
}
}'const origin = await client.origins.create({
name: "Cloudflare R2 Outputs",
permissions: [OriginPermission.READ, OriginPermission.WRITE],
r2: {
bucket: "acme-transcoded-outputs",
accountId: "f037e1abcd0987654321fedcba012345",
credentials: {
accessKeyId: "...",
secretAccessKey: "...",
},
},
});origin = client.origins.create(
name="Cloudflare R2 Outputs",
permissions=["read", "write"],
r2={
"bucket": "acme-transcoded-outputs",
"account_id": "f037e1abcd0987654321fedcba012345",
"credentials": {
"access_key_id": "...",
"secret_access_key": "...",
},
},
)origin, err := client.Origins.Create(ctx, &transcodely.OriginCreateParams{
Name: "Cloudflare R2 Outputs",
Permissions: []transcodely.OriginPermission{transcodely.OriginPermissionRead, transcodely.OriginPermissionWrite},
R2: &transcodely.R2OriginConfig{
Bucket: "acme-transcoded-outputs",
AccountId: "f037e1abcd0987654321fedcba012345",
Credentials: &transcodely.S3Credentials{
AccessKeyId: "...",
SecretAccessKey: "...",
},
},
})Set jurisdiction to "eu" or "fedramp" for data residency. Omit it (or set "default") for the global jurisdiction.
For custom domains or jurisdictions not yet enumerated, supply an explicit endpoint URL (e.g. https://media.example.com) inside r2 instead of account_id. Exactly one of account_id or endpoint must be set. When you supply an endpoint, jurisdiction must be omitted entirely — setting any jurisdiction value alongside endpoint (including "default") is rejected by the r2.jurisdiction_requires_account_id validation rule and the RPC returns InvalidArgument.
Creating an HTTP Origin
HTTP origins are read-only and are typically used for fetching source videos from CDNs or public URLs. Supply an http block with a base_url and, if the endpoint requires authentication, request headers to send on every fetch:
curl -X POST https://api.transcodely.com/transcodely.v1.OriginService/Create
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{
"name": "CDN Source",
"permissions": ["read"],
"http": {
"base_url": "https://cdn.example.com/videos/",
"credentials": {
"headers": {
"Authorization": "Bearer cdn_token_abc123"
}
}
}
}'const origin = await client.origins.create({
name: "CDN Source",
permissions: [OriginPermission.READ],
http: {
baseUrl: "https://cdn.example.com/videos/",
credentials: {
headers: {
Authorization: "Bearer cdn_token_abc123",
},
},
},
});origin = client.origins.create(
name="CDN Source",
permissions=["read"],
http={
"base_url": "https://cdn.example.com/videos/",
"credentials": {
"headers": {
"Authorization": "Bearer cdn_token_abc123",
},
},
},
)origin, err := client.Origins.Create(ctx, &transcodely.OriginCreateParams{
Name: "CDN Source",
Permissions: []transcodely.OriginPermission{transcodely.OriginPermissionRead},
Http: &transcodely.HttpOriginConfig{
BaseUrl: "https://cdn.example.com/videos/",
Credentials: &transcodely.HttpCredentials{
Headers: map[string]string{
"Authorization": "Bearer cdn_token_abc123",
},
},
},
})Origin Status
| Status | Description |
|---|---|
active | Credentials validated, origin is ready to use |
failed | Credential validation failed — check validation_error for details |
archived | Soft-deleted, cannot be used for new jobs |
Permissions
When creating an origin, you declare what permissions the credentials should have:
| Permission | Description | Required For |
|---|---|---|
read | List and download objects | Input origins |
write | Upload objects | Output origins |
An origin used for both input and output should have both read and write permissions. HTTP origins only support read.
Path Templates
Origins can define a path_template that controls where output files are written. Templates support variables that are resolved at job creation time:
| Variable | Example Value | Description |
|---|---|---|
{job_id} | job_a1b2c3d4e5f6 | Job identifier |
{video_id} | promo-2026 | Customer-provided video identifier; falls back to {job_id} when unset |
{timestamp} | 2026-01-15T10-30-00Z | ISO timestamp (file-safe) |
{date} | 2026-01-15 | Date in YYYY-MM-DD format |
{uuid} | f47ac10b-58cc-4372-a567-0e02b2c3d479 | Random UUID generated per occurrence |
{output_id} | out_xyz789 | Output identifier |
{output_index} | 0 | Zero-based index of the output within the job |
{preset_id} | pst_x9y8z7w6v5 | Preset identifier |
{codec} | h264 | Video codec |
{resolution} | 1080p | Resolution preset |
{bitrate} | 5000k | Output video bitrate |
{format} | mp4 | Output container format |
{framerate} | 30 | Output frame rate |
{quality} | standard | Quality tier |
{segment_index} | 0 | Zero-based segment index (HLS/DASH) |
{segment_number} | 1 | One-based segment number (HLS/DASH) |
{segment_time} | 12 | Segment start time in seconds (HLS/DASH) |
{thumb_index} | 0 | Zero-based thumbnail index |
{thumb_time} | 30 | Thumbnail timestamp in seconds |
{thumb_time_ms} | 30000 | Thumbnail timestamp in milliseconds |
The {segment_*} and {thumb_*} variables only carry meaningful values when naming HLS/DASH segments or thumbnails; {type} is accepted as a legacy alias for {format}.
Example: a path template of {date}/{job_id}/{codec}_{resolution} produces paths like 2026-01-15/job_a1b2c3/h264_1080p.mp4.
The base_path is prepended to all resolved paths. If base_path is videos/ and the resolved template is 2026-01-15/job_a1b2c3/output.mp4, the final path becomes videos/2026-01-15/job_a1b2c3/output.mp4.
Validation
Credentials are automatically validated when an origin is created or when credentials are updated. You can also re-validate at any time to check for permission drift — the call reports whether the credentials can read and write, along with the timestamp of the check:
curl -X POST https://api.transcodely.com/transcodely.v1.OriginService/Validate
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{ "id": "ori_x9y8z7w6v5" }'const result = await client.origins.validate({ id: "ori_x9y8z7w6v5" });
console.log(result.validation?.success, result.validation?.canWrite);result = client.origins.validate(id="ori_x9y8z7w6v5")
print(result.validation.success, result.validation.can_write)validation, err := client.Origins.Validate(ctx, "ori_x9y8z7w6v5")
fmt.Println(validation.GetSuccess(), validation.GetCanWrite()){
"origin": { "id": "ori_x9y8z7w6v5", "status": "active", "..." : "..." },
"validation": {
"success": true,
"can_read": true,
"can_write": true,
"validated_at": "2026-01-15T10:30:00Z"
}
}Credential Security
Credentials are encrypted at rest and never returned in API responses. After creation, you can only see that credentials exist — not their values. To update credentials, provide new ones through the Update endpoint.
Archiving Origins
Archived origins cannot be used for new jobs, but existing job data stored in the origin remains accessible:
curl -X POST https://api.transcodely.com/transcodely.v1.OriginService/Archive
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: org_a1b2c3d4e5"
-H "Content-Type: application/json"
-d '{ "id": "ori_x9y8z7w6v5" }'const origin = await client.origins.archive("ori_x9y8z7w6v5");origin = client.origins.archive("ori_x9y8z7w6v5")err := client.Origins.Archive(ctx, "ori_x9y8z7w6v5")