Content Delivery API
How to authenticate
All content delivery API methods, except /v1/access_token, require a bearer-type Authorization header containing an access token. To acquire an access token you should call /v1/access_token API method described below.
Passing Authorization header with cURL:
curl -X $HTTP_METHOD -H "Authorization: Bearer $ACCESS_TOKEN" https://api-cdn.qencode.com/v1/$OBJECT
All requests must be made over HTTPS.
Getting Access Token
Qencode requires API keys to generate access tokens that are used to authenticate all of your live stream requests. To get an access_token, you must provide your API key in the authorization header of the request.
You can view and manage the API keys associated with your projects inside of your Qencode Account.
Access token is valid for 24 hours.
For live-streaming, an API key is assigned to each Project created in your Qencode account. After logging into your account, you can manage your API keys on the Live Streaming Projects page, as well as track the usage of each Project on the Statistics page.
After API key authentication is complete, you will receive this session based token, which can be used to call all other live streaming API methods.
Replace the 'your_api_key' value below with your API key. You can find you API key in your Live Streaming Project within your account.
curl -X POST https://api-live.qencode.com/v1/access_token/your_api_key
Token returned should be passed in Authorization header to all other live streaming API methods described below
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyjpYXQiOjE2MjgxNTgyNJMsInN1YiI6MTMwLCJleHAiOjE2MjgyNDQ2NjN9.SxS3zLx2CZbZ9ylTpd25kj9el6_4TqqTWUA9RT2iJ9I"
}
Creating a Playback Domain
You can create custom domains and use them for live-streaming or media storage playback.
A custom domain can either be used for live-streaming or media-storage, so you'll need to create separate domains for live-streaming and media storage playback in case you need to support both.
You won't be able to update your domain object after it's created. Instead you can delete it and create another domain.
You can create up to 20 custom domains for playback. In case this does not cover your use case you can contact our support.
curl -X POST 'https://api-cdn.qencode.com/v1/domains' \
-H 'Authorization: Bearer $ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data-raw '{"name": "live.mydomain.com", "type": "live"}'
{
"domain": {
"created": "2023-06-07 14:17:25",
"id": "aeac03d6-beb0-4638-a094-fbc35653f931",
"type": "live",
"domain_name": "live.mydomain.com"
}
}
Getting a Playback Domain Data
Gets a playback domain information.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X GET https://api-cdn.qencode.com/v1/domains/$DOMAIN_ID
{
"domain": {
"created": "2023-06-07 14:17:25",
"id": "aeac03d6-beb0-4638-a094-fbc35653f931",
"type": "live",
"domain_name": "live.mydomain.com"
}
}
Listing Playback Domains
Gets a list of user domains.
In case no filter specified, contains list of all user domains.
You can optionally pass the domain type in the last URL segment. E.g. `GET /v1/domains/live` will return the list of live-streaming domains.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X GET https://api-cdn.qencode.com/v1/domains
{
"domains": [
{
"created": "2023-06-07 14:17:25",
"id": "aeac03d6-beb0-4638-a094-fbc35653f931",
"type": "live",
"domain_name": "live.mydomain.com"
}
]
}
Deleting a Playback Domain
Deletes a playback domain.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X DELETE https://api-cdn.qencode.com/v1/domains/$DOMAIN_ID
{
"status": "ok"
}
Creating a TLS Subscription
You can create automatically managed TLS subscriptions for custom domains and use them for live-streaming or media storage playback.
TLS subscription is a Let's Encrypt TLS certificate automatically issued and renewed for a playback domain.
Use this as a quick and easy way to setup TLS/HTTPS for your domain.
TLS subscription is a quick and easy way to setup TLS for your custom playback domain.
curl -X POST 'https://api-cdn.qencode.com/v1/tls/subscriptions' \
-H 'Authorization: Bearer $ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data-raw '{"domain_id": "aeac03d6-beb0-4638-a094-fbc35653f931"}'
{
"tls_subscription": {
"status": "pending",
"created": "2023-06-12 15:10:48",
"domain_id": "aeac03d6-beb0-4638-a094-fbc35653f931",
"message": "TLS subscription created. You should verify your domain ownership. Create a CNAME for 'live.mydomain.com' and point it to 'j.sni.global.fastly.net.'.",
"id": "7d819560-6220-4b76-bf9a-3784a64ac7ff"
}
}
Getting a TLS subscription Data
Gets a TLS subscription information.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X GET https://api-cdn.qencode.com/v1/tls/subscriptions/$TLS_SUBSCRIPTION_ID
{
"tls_subscription": {
"status": "pending",
"created": "2023-06-12 15:10:48",
"domain_id": "aeac03d6-beb0-4638-a094-fbc35653f931",
"id": "7d819560-6220-4b76-bf9a-3784a64ac7ff"
}
}
Listing TLS subscriptions
Gets a list of user TLS subscriptions.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X GET https://api-cdn.qencode.com/v1/tls/subscriptions
{
"tls_subscriptions": [
{
"status": "pending",
"created": "2023-06-12 15:10:48",
"id": "7d819560-6220-4b76-bf9a-3784a64ac7ff",
"domain_id": "aeac03d6-beb0-4638-a094-fbc35653f931"
}
]
}
Deleting a TLS subscription
Deletes a TLS subscription.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X DELETE https://api-cdn.qencode.com/v1/tls/subscriptions/$TLS_SUBSCRIPTION_ID
{
"status": "ok"
}
Creating a Signing key
You can create signing keys and use them for live-streaming or media storage playback authentication.
A signing key can either be used for a Live stream, a Live stream Playback domain or a Media storage bucket.
You won't be able to get the private key data using the /v1/signing-keys after the signing key created.
You should save private key data you get from the response of /v1/signing-keys and store it in a secure place.
curl -X POST 'https://api-cdn.qencode.com/v1/signing-keys' \
-H 'Authorization: Bearer $ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data-raw '{"stream_id": "1f1ab6a3-d867-4787-b882-7f2fb1203634"}'
{
"signing_key": {
"stream_id": "1f1ab6a3-d867-4787-b882-7f2fb1203634",
"private_key": "-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEA4DAKSi4F8yQTt1L4N9s9Wt+3hBoHt5KMs14jy37YNnqxaIzJ
...
1c4v1P/wPr2sqepof19dbacmGcxplhHJ4d3BXAdInxYuGrgerShJXy5icFWf8A==
-----END RSA PRIVATE KEY-----
",
"created": "2023-06-12 15:00:40",
"id": "ec25a6a6-b31e-4ac9-af01-711460421c6b",
"name": null
}
}
Getting a Signing key data
Gets a signing key information.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X GET https://api-cdn.qencode.com/v1/signing-keys/$SIGNING_KEY_ID
{
"signing_key": {
"stream_id": "1f1ab6a3-d867-4787-b882-7f2fb1203634",
"created": "2023-06-12 15:00:40",
"name": "My Signing Key",
"id": "ec25a6a6-b31e-4ac9-af01-711460421c6b"
}
}
Listing Signing keys
Gets a list of user signing keys.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X GET https://api-cdn.qencode.com/v1/signing-keys
{
"signing_keys": [
{
"created": "2023-06-07 14:17:25",
"id": "e52f59d7-e65c-4036-81c6-0163403a1a88",
"domain_id": "aeac03d6-beb0-4638-a094-fbc35653f931"
},
{
"stream_id": "1f1ab6a3-d867-4787-b882-7f2fb1203634",
"created": "2023-06-12 15:00:40",
"id": "ec25a6a6-b31e-4ac9-af01-711460421c6b",
"name": null
}
]
}
Updating a Signing key
Updates the signing key name.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X PUT https://api-cdn.qencode.com/v1/signing-keys/$SIGNING_KEY_ID \
-H 'Content-Type: application/json' \
--data-raw '{"name": "My signing key"}'
{
"signing_key": {
"stream_id": "1f1ab6a3-d867-4787-b882-7f2fb1203634",
"created": "2023-06-12 15:00:40",
"id": "ec25a6a6-b31e-4ac9-af01-711460421c6b",
"name": "My signing key"
}
}
Deleting a Signing key
Deletes a signing key.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X DELETE https://api-cdn.qencode.com/v1/signing-keys/$SIGNING_KEY_ID
{
"status": "ok"
}
Setting a media storage bucket access policy
Sets bucket access policy.
Policy name can either be 'public' or 'authenticated'. Buckets are set to public access policy by default.
In case authenticated policy is set for a bucket all content inside the bucket can be accessed with a signed token passed in the URL.
See Signed tokens for more information.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X PUT https://api-cdn.qencode.com/v1/buckets/$BUCKET_NAME/policy/$POLICY_NAME
{
"status": "ok"
}
Setting a media storage bucket CORS policy
Sets bucket CORS policy.
Accepts an object where attribute names are CORS headers and values a corresponding CORS header values
Supported CORS headers are listed below
Indicates how long the results of a preflight request (that is the information contained in the Access-Control-Allow-Methods and Access-Control-Allow-Headers headers) can be cached.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X POST https://api-cdn.qencode.com/v1/buckets/$BUCKET_NAME/cors -H 'Content-Type: application/json' --data '{"Access-Control-Allow-Origin": "*"}'
{
"status": "ok"
}
Getting a media storage bucket CORS policy
Gets bucket CORS policy.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X GET https://api-cdn.qencode.com/v1/buckets/$BUCKET_NAME/cors
{
"Access-Control-Allow-Origin": "https://media-storage.us-west.qencode.com",
"Access-Control-Allow-Methods": "GET, PUT, POST, DELETE",
"Access-Control-Max-Age": "1600",
"Access-Control-Allow-Credentials": "true"
}
Deleting a media storage bucket CORS policy
Deletes bucket CORS policy.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X DELETE https://api-cdn.qencode.com/v1/buckets/$BUCKET_NAME/cors
{
"status": "ok"
}
Deleting a media storage bucket CORS header
Removes a header from the bucket CORS policy.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X DELETE https://api-cdn.qencode.com/v1/buckets/$BUCKET_NAME/cors/Access-Control-Allow-Origin
{
"status": "ok"
}
Purging CDN cache for a bucket
Purges CDN cache for all objects in a bucket.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
-X POST https://api-cdn.qencode.com/v1/buckets/my-bucket/cache/purge_all
{
"status": "ok"
}
Purging CDN cache for an object in a bucket
Purges CDN cache for a single object in a bucket.
curl -H "Authorization: Bearer $ACCESS_TOKEN"
--location "https://api-cdn.qencode.com/v1/buckets/my-bucket/cache/purge" -H 'Content-Type: application/json'
--data '{"path": "path/to/object"}'
{
"status": "ok"
}
Generate signed tokens
Signed tokens are JSON Web Tokens (JWT) used to authenticate requests to Qencode Content delivery service.
You can use it to authenticate access to live-streams playback or to a content in your Qencode media storage bucket.
To authenticate the request a signed token should be added to the resource url using the auth query string param. For example, you can authenticate live-stream playback URL request the following way:
https://play-$PLAYBACK_ID.qencode.com/qhls/qlive/playlist.m3u8?auth=$TOKEN
where $PLAYBACK_ID is your live stream playback ID.
For live streams using the custom domain for playback the URL will look like this:
https://$CUSTOM_DOMAIN/$PLAYBACK_ID.m3u8?auth=$TOKEN
where $CUSTOM_DOMAIN is your custom Playback domain name.
To authenticate a request to Qencode media storage bucket you can build a URL like this:
https://$BUCKET.media-storage.$REGION.qencode.com/path/to/video.mp4?auth=$TOKEN
where $BUCKET is your media storage bucket name and $REGION is Qencode media storage region. Currently available Qencode media storage regions are us-west and eu-central.
To authenticate a request to Qencode media storage bucket using your custom domain you can build a URL like this:
https://$CUSTOM_DOMAIN/path/to/video.mp4?auth=$TOKEN
where $CUSTOM_DOMAIN is your custom domain name.
Signed token structure
The structure of a signed token includes a list of headers and payload values. Please see the list of supported headers and payload values below. You can also add your custom payload values to a JWT.
For Live streaming, uri should be set to the stream playback ID.
For media storage uri should be a key in the bucket or a key prefix. Setting uri to an empty string authorizes the request to access any resource in the bucket.
If not specified, token does not expire.
A timestamp that indicates the time before which the token must not be accepted.
Code examples
Here are code samples in different programming languages you can use to generate a signed token:
Generating a signed token
import jwt
import time
def generate_signed_token(signing_key_id, private_key, uri, exp=3600, nbf=None):
payload = {
'uri': uri
}
if exp > 0:
payload['exp'] = int(time.time()) + exp
if nbf is not None:
payload['nbf'] = nbf
headers = {
'alg': 'RS256',
'typ': 'JWT',
'kid': signing_key_id
}
token = jwt.encode(payload, private_key, algorithm="RS256", headers=headers)
return token
# you can get signing key data from the response of https://api-cdn.qencode.com/v1/signing_keys endpoint
signing_key = {
'id' = 'your_signing_key_id',
'private_key' = 'your_signing_key_private_key'
}
# generate token for Live stream playback
stream_playback_id = 'your_stream_playback_id'
token = generate_signed_token(signing_key['id'], signing_key['private_key'], playback_id)
<?php
/*
Please note that in order to use JWT in PHP, you need to install the "firebase/php-jwt" library via Composer.
You can do that with the following command:
composer require firebase/php-jwt
*/
require_once('vendor/autoload.php');
use FirebaseJWTJWT;
function generateSignedToken($signingKeyId, $privateKey, $uri, $exp = 3600, $nbf = null)
{
$payload = [
'uri' => $uri
];
if ($exp > 0) {
$payload['exp'] = time() + $exp;
}
if ($nbf !== null) {
$payload['nbf'] = $nbf;
}
$headers = [
'alg' => 'RS256',
'typ' => 'JWT',
'kid' => $signingKeyId
];
$token = JWT::encode($payload, $privateKey, 'RS256', null, $headers);
return $token;
}
// you can get signing key data from the response of https://api-cdn.qencode.com/v1/signing_keys endpoint
$signingKey = [
'id' => 'your_signing_key_id',
'private_key' => 'your_signing_key_private_key'
];
// generate token for Live stream playback
$streamPlaybackId = 'your_stream_playback_id';
$token = generateSignedToken($signingKey['id'], $signingKey['private_key'], $streamPlaybackId);
echo $token;
?>
const jwt = require('jsonwebtoken');
function generateSignedToken(signingKeyId, privateKey, uri, exp = 3600, nbf = null) {
let payload = {
uri: uri,
};
if (exp > 0) {
payload.exp = Math.floor(Date.now() / 1000) + exp;
}
if (nbf !== null) {
payload.nbf = nbf;
}
let headers = {
alg: 'RS256',
typ: 'JWT',
kid: signingKeyId,
};
let token = jwt.sign(payload, privateKey, { algorithm: 'RS256', header: headers });
return token;
}
// you can get signing key data from the response of https://api-cdn.qencode.com/v1/signing_keys endpoint
let signingKey = {
id: 'your_signing_key_id',
privateKey: 'your_signing_key_private_key',
};
// generate token for Live stream playback
let streamPlaybackId = 'your_stream_playback_id';
let token = generateSignedToken(signingKey.id, signingKey.privateKey, streamPlaybackId);
console.log(token);
using System;
using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
class Program
{
static void Main()
{
string signingKeyId = "your_signing_key_id";
string privateKey = "your_signing_key_private_key"; // You might need to convert this to a SecurityKey
string uri = "your_stream_playback_id";
int exp = 3600;
int? nbf = null;
var token = GenerateSignedToken(signingKeyId, privateKey, uri, exp, nbf);
Console.WriteLine(token);
}
static string GenerateSignedToken(string signingKeyId, string privateKey, string uri, int exp, int? nbf)
{
var claims = new List<Claim>
{
new Claim("uri", uri)
};
if (exp > 0)
{
var expTime = DateTime.UtcNow.AddSeconds(exp);
claims.Add(new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(expTime).ToUnixTimeSeconds().ToString()));
}
if (nbf != null)
{
claims.Add(new Claim(JwtRegisteredClaimNames.Nbf, nbf.ToString()));
}
var securityKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(privateKey)); // might need to be adjusted based on your key
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);
var header = new JwtHeader(credentials);
header.Add("kid", signingKeyId);
var payload = new JwtPayload(claims);
var secToken = new JwtSecurityToken(header, payload);
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(secToken);
}
}
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.util.Date;
public class Main {
public static void main(String[] args) {
String signingKeyId = "your_signing_key_id";
String privateKeyStr = "your_signing_key_private_key"; // You might need to convert this to a Key
String uri = "your_stream_playback_id";
long exp = 3600;
Long nbf = null;
String token = generateSignedToken(signingKeyId, privateKeyStr, uri, exp, nbf);
System.out.println(token);
}
private static String generateSignedToken(String signingKeyId, String privateKeyStr, String uri, long exp, Long nbf) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
byte[] privateKeyBytes = privateKeyStr.getBytes(StandardCharsets.UTF_8);
Key signingKey = Keys.hmacShaKeyFor(privateKeyBytes);
JwtBuilder builder = Jwts.builder()
.setId(signingKeyId)
.claim("uri", uri)
.signWith(signingKey, SignatureAlgorithm.HS256);
if (exp > 0) {
long expMillis = nowMillis + exp * 1000;
Date expDate = new Date(expMillis);
builder.setExpiration(expDate);
}
if (nbf != null) {
long nbfMillis = nbf * 1000;
Date nbfDate = new Date(nbfMillis);
builder.setNotBefore(nbfDate);
}
return builder.compact();
}
}
require 'jwt'
require 'time'
def generate_signed_token(signing_key_id, private_key, uri, exp=3600, nbf=nil)
payload = {
'uri' => uri
}
if exp > 0
payload['exp'] = Time.now.to_i + exp
end
if !nbf.nil?
payload['nbf'] = nbf
end
headers = {
'alg' => 'RS256',
'typ' => 'JWT',
'kid' => signing_key_id
}
token = JWT.encode payload, private_key, 'RS256', headers
return token
end
# you can get signing key data from the response of https://api-cdn.qencode.com/v1/signing_keys endpoint
signing_key = {
'id' => 'your_signing_key_id',
'private_key' => 'your_signing_key_private_key'
}
# generate token for Live stream playback
stream_playback_id = 'your_stream_playback_id'
token = generate_signed_token(signing_key['id'], signing_key['private_key'], stream_playback_id)
puts token