Quickstart¶
This page walks through submitting a real job end-to-end. By the end you will have transcoded a file with ffmpeg, then packaged an encrypted DASH stream with shaka-packager and DRMtoday — both from your terminal, using curl.
If the vocabulary feels unfamiliar (organization, job, task, tool, secret), skim Concepts first.
Prerequisites¶
You will be juggling three independent sets of credentials — make sure you can tell them apart before you start:
Your VTK API access key (
access_key_id,secret_access_key,user_urn) — proves to VTK who you are. Created once at https://account.castlabs.com/account → API Keys → [New API Key]. The dialog shows all three values once; copy them now. See Setup → Authentication.AWS credentials for your S3 bucket — let the VTK worker read or write objects in your AWS account. Three ways to provide them, you only need one:
Org secret (used in the examples below): create an access key for an IAM user in the AWS console (IAM → Users → Security credentials → Create access key), then save it under Secrets in the VTK web UI.
Assume role: create an IAM role in your account that trusts the VTK worker role; pass its ARN as
role_arnon the job (Security → IAM).Credential dispenser: any HTTPS endpoint you host that returns AWS credentials per request — short-lived from an STS broker, static, mocked, whatever fits (Security → Credential dispenser).
DRMtoday credentials (Example B only) —
merchantandusercome from your DRMtoday account portal; the matchingpasswordmust be stored as a VTK org secret and referenced as{name}— never inlined.
You also need an organization_urn (the VTK org you’re a member of). Find it in the web UI at https://fe.vtk.castlabs.com/ → Organizations; it looks like urn:janus:organization:acme.
In the examples below, replace these placeholders with your values:
urn:janus:organization:acme— yourorganization_urn.acme-bucket— your S3 bucket.{acme-aws-access-keys}— name of the org secret holding your AWS access keys (create it under Secrets in the UI).{acme-drmtoday-password}— name of the org secret holding the DRMtoday password.acme::opsandmerchant: acme— your DRMtoday user and merchant.
Step 1 — Get a bearer token¶
VTK authenticates every request with a Castlabs OAuth bearer token. To get one, POST your API access key to the credential-exchange endpoint at https://auth.castlabs.com/api/v1/keypair/credentialexchange; the body is signed with an HMAC chain derived from your secret_access_key and user_urn. Setup → Authentication has the full request shape and a ready-to-run Python implementation — copy it, run it, and pick the access_token out of the response.
The rest of this page assumes you’ve stored it in $TOKEN:
TOKEN=eyJraWQiOi… # access_token from the exchange
ORG=urn:janus:organization:acme # your organization_urn
Step 2 — Submit a job¶
Jobs are submitted with POST /o/{organization_urn}/jobs. The body is a JSON object with at least tasks; common optional fields are region, tags, notify, and role_arn.
curl -X POST "https://api.vtk.castlabs.com/o/$ORG/jobs" \
-H "Authorization: Bearer $TOKEN" \
-H "x-castlabs-organization: $ORG" \
-H "Content-Type: application/json" \
-d @job.json
A 201 Created response returns the full job, including the assigned id and the initial state of every task. Save the id — you’ll use it to poll status and fetch logs.
{ "id": "abcDEFGHIjk", "status": 0, "tasks": [ { "task_id": 12345, "tool": "storage:get", "status": 0 }, … ] }
Step 3 — Poll the job¶
JOB_ID=abcDEFGHIjk
while :; do
status=$(curl -s "https://api.vtk.castlabs.com/o/$ORG/jobs/$JOB_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "x-castlabs-organization: $ORG" | jq -r '.status')
echo "status=$status"
case "$status" in 2|3|4|6) break ;; esac
sleep 10
done
Status codes: 0 submitted, 5 waiting for resources, 1 running, 2 completed, 3 failed, 4 aborted, 6 resource terminated. See Status for the full table.
You can also watch the job live in the web UI at https://fe.vtk.castlabs.com/ → Jobs, or skip polling entirely by adding "notify": "mailto:you@example.com" (or an HTTPS callback) to the submission body — VTK then pushes a notification when the job reaches a terminal state.
Step 4 — Fetch task logs¶
If a task fails, the log usually tells you why:
curl "https://api.vtk.castlabs.com/o/$ORG/jobs/$JOB_ID/tasks/12345/log" \
-H "Authorization: Bearer $TOKEN" \
-H "x-castlabs-organization: $ORG"
{ "count": 2, "results": [
{ "timestamp": 1607374149000, "message": "ffmpeg version 6.0 …" },
{ "timestamp": 1607374152000, "message": "frame= 150 fps= 75 …" }
] }
Example A — Transcode an MP4 (S3 → ffmpeg → S3)¶
A three-task job: download a source file from S3, transcode it with ffmpeg, upload the result to a per-job folder.
job.json:
{
"tags": ["quickstart", "transcode"],
"region": "aws:eu-west-1",
"tasks": [
{
"tool": "storage:get",
"parameters": {
"location": "s3://{acme-aws-access-keys}@acme-bucket/in/",
"files": ["source.mov"]
}
},
{
"tool": "ffmpeg:cmd",
"parameters": {
"arguments": [
"-y", "-i", "source.mov",
"-c:v", "libx264", "-preset", "veryfast",
"-b:v", "1500k", "-maxrate", "1500k", "-bufsize", "3000k",
"-c:a", "aac", "-b:a", "128k",
"source_720p.mp4"
]
}
},
{
"tool": "storage:put",
"parameters": {
"location": "s3://{acme-aws-access-keys}@acme-bucket/out/{job_id}/",
"files": ["source_720p.mp4"]
}
}
]
}
Notes:
The S3 URL form
s3://{secret-name}@bucket/pathreferences AWS credentials stored as an org secret. Two alternatives: setrole_arnat the job level and let VTK assume an IAM role in your AWS account (Security → IAM), or have eachstorage:*task fetch credentials from an HTTPS endpoint you host (Security → Credential dispenser).{job_id}in the output path is substituted by the runner; the resulting S3 prefix will bes3://acme-bucket/out/abcDEFGHIjk/.source.movwritten bystorage:getis read byffmpeg:cmdfrom the same working directory (/job/inside the worker);source_720p.mp4written byffmpeg:cmdis read bystorage:putthe same way.
Submit it with the curl from Step 2 (-d @job.json), then poll as in Step 3. On success, the encoded MP4 lands under s3://acme-bucket/out/abcDEFGHIjk/.
Example B — Encrypted DASH with DRMtoday + Shaka¶
The shaka:package_dash tool can contact DRMtoday directly: give it asset_id, environment, merchant, user, password, and a list of drmkeys, and it will fetch CENC keys and produce an encrypted DASH manifest in one step.
job.json:
{
"tags": ["quickstart", "dash", "drm"],
"region": "aws:eu-west-1",
"tasks": [
{
"tool": "storage:get",
"parameters": {
"location": "s3://{acme-aws-access-keys}@acme-bucket/in/",
"files": ["video_1080p.mp4", "audio_eng.mp4"]
}
},
{
"tool": "shaka:package_dash",
"parameters": {
"inputs": [
{
"input_file": "video_1080p.mp4",
"stream_selector": "video",
"output": "video.mp4",
"output_format": "mp4",
"bandwidth": "2000000",
"key_select": "video_key"
},
{
"input_file": "audio_eng.mp4",
"stream_selector": "audio",
"language": "eng",
"output": "audio.mp4",
"output_format": "mp4",
"bandwidth": "128000",
"key_select": "audio_key"
}
],
"output_dir": "dash",
"mpd_output": "dash.mpd",
"fragment_duration": "2",
"segment_duration": "2",
"protection_scheme": "cenc",
"protection_systems": ["Widevine", "PlayReady"],
"enable_raw_key_encryption": "True",
"asset_id": "quickstart_{job_id}",
"environment": "STAGING",
"merchant": "acme",
"user": "acme::ops",
"password": "{acme-drmtoday-password}",
"drmkeys": [
{ "key_label": "video_key", "key_streamtype": "VIDEO" },
{ "key_label": "audio_key", "key_streamtype": "AUDIO" }
]
}
},
{
"tool": "storage:put",
"parameters": {
"location": "s3://{acme-aws-access-keys}@acme-bucket/dash/{job_id}/",
"files": ["dash/*"]
}
}
]
}
Notes:
Each input’s
key_selectmatches akey_labelindrmkeys. Video and audio use separate keys here (video_keywithkey_streamtype: VIDEO,audio_keywithkey_streamtype: AUDIO) so each track can be unlocked independently downstream.environmentisSTAGINGorPRODUCTIONand selects the DRMtoday environment.The DRMtoday password must be an org secret. Storing it in plaintext is rejected by the VTK admin UI; see Security → Passwords.
If you only need to register an explicit content key with DRMtoday before packaging, prepend a
drmtoday:ingest-cenc-keytask. It emits the key material into the job environment under a configurableenv_prefixfor downstream tools to pick up. See Tools → DRMtoday for the parameter set.
What’s next¶
API → Jobs — full endpoint reference, including job bundles, abort/restart/resubmit, and metrics.
API → Status — completion notifications via email or HTTP POST callback.
Security → IAM — cross-account S3 access with
role_arn.Tools — the complete catalog of tools and their parameters.