Authentication

All Video Toolkit users are managed by Castlabs’ single sign-on system. The Video Toolkit API at https://api.vtk.castlabs.com accepts a Bearer token in the Authorization header on every request:

Authorization: Bearer <access_token>

The access token is an OAuth token obtained from the Castlabs identity provider. There are two ways to get one:

  1. Interactive login — the Web UI handles this for you automatically.

  2. API access keys — for programmatic and automated use (cron jobs, CI, scripts).

This page describes the API access key flow.

API access keys

API access keys are attached to your user account and are independent from the organizations you are a member of. Each key consists of three values:

  • access_key_id (e.g. urn:janus:accesskey:...)

  • secret_access_key

  • user_urn (e.g. urn:janus:user:...)

Creating an API access key

API access keys are managed centrally for your Castlabs account, not per product. Create and manage them at https://account.castlabs.com/account, which also documents the creation flow and the two-key rotation limit.

The page issues three values — access_key_id, secret_access_key, and user_urn — that you then use with the credential-exchange flow described below.

Exchanging API access keys for OAuth tokens

The API access keys are not sent directly to the Video Toolkit API. They are exchanged for a short-lived OAuth access_token at the Castlabs auth endpoint:

POST https://auth.castlabs.com/api/v1/keypair/credentialexchange

The request body is a JSON payload, and the request is signed with an HMAC chain derived from your Secret Access Key, User URN, and the current date.

Authentication flow

  1. Create a JSON payload with your Access Key ID and an ISO‑8601 timestamp.

  2. Derive a signing key from your Secret Access Key and User URN.

  3. Sign the payload with HMAC‑SHA1 and put the Base64 result in the X-Castlabs-Keypair-Signature header.

  4. POST the payload. The response contains an id_token, access_token, refresh_token, and expires_at.

Use the access_token as the Bearer token for subsequent calls to https://api.vtk.castlabs.com.

Python example

import base64
import json
import requests
from datetime import datetime
from hashlib import sha256, sha1
from hmac import HMAC

ACCESS_KEY_ID = "urn:janus:accesskey:your-key-id"
SECRET_ACCESS_KEY = "your-secret-access-key"
USER_URN = "urn:janus:user:your-user-id"

SIGNING_KEY_NAME = b"castLabs-api_auth"
API_ENDPOINT = "https://auth.castlabs.com/api/v1/keypair/credentialexchange"


def get_signing_key(secret_access_key: str, user_urn: str, timestamp: str) -> bytes:
    secret_token = HMAC(("castLabs " + secret_access_key).encode(),
                       timestamp.encode(), sha256).digest()
    user_token = HMAC(secret_token, user_urn.encode(), sha256).digest()
    return HMAC(user_token, SIGNING_KEY_NAME, sha256).digest()


payload = {
    "access_key_id": ACCESS_KEY_ID,
    "timestamp": datetime.utcnow().isoformat(),
}
payload_str = json.dumps(payload)

timestamp = datetime.utcnow().strftime("%Y-%m-%d")
signing_key = get_signing_key(SECRET_ACCESS_KEY, USER_URN, timestamp)
signature = base64.b64encode(
    HMAC(signing_key, payload_str.encode(), sha1).digest()
)

response = requests.post(
    API_ENDPOINT,
    headers={"X-Castlabs-Keypair-Signature": signature.decode()},
    json=payload,
)

tokens = response.json()
print(f"ID Token:      {tokens['id_token']}")
print(f"Access Token:  {tokens['access_token']}")
print(f"Refresh Token: {tokens['refresh_token']}")
print(f"Expires At:    {tokens['expires_at']}")

The HMAC chain is:

  1. secret_token = HMAC_SHA256("castLabs " + secret_access_key, date)date formatted as YYYY-MM-DD (UTC).

  2. user_token   = HMAC_SHA256(secret_token, user_urn).

  3. signing_key  = HMAC_SHA256(user_token, "castLabs-api_auth").

  4. signature    = base64( HMAC_SHA1(signing_key, payload_json) ).

Refreshing tokens

Access tokens are short-lived (expires_at in the response). Instead of running the full signature exchange every time, use the refresh_token:

POST https://auth.castlabs.com/api/v1/keypair/refreshcredentials

with the refresh token in the body. This avoids regenerating signatures and reduces load on the credential-exchange endpoint.

Verifying your setup with cURL

Once you have an access_token, verify it against the Video Toolkit API by listing jobs in one of your organizations:

curl https://api.vtk.castlabs.com/o/<organization_urn>/jobs \
  -H 'Authorization: Bearer <access_token>' \
  -H 'x-castlabs-organization: <organization_urn>'

A 200 OK response with a paginated job list confirms that authentication, the organization URN, and your IAM policy are all working.

Next topic: Jobs
Previous topic: Concepts