Publish to Rad
Upload video from a URL or local file, transcode it, and publish on Rad TV.
Publishing a video to Rad TV takes your source file through ingestion, transcoding, and publication. You can do this with a single compound mutation or step-by-step for full control.
Option 1: Single Call with publishVideo
The publishVideo mutation handles the entire pipeline. It supports two modes:
Mode 1: Server-Side Download (URL)
Provide a sourceUrl and the server downloads, ingests, transcodes, and publishes the video for you.
curl -X POST https://api.rad.live/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"query": "mutation($input: PublishVideoInput!) { publishVideo(input: $input) { content { id metadata { title } } job { id status } } }",
"variables": {
"input": {
"title": "My Video",
"summary": "A great video about something.",
"sourceUrl": "https://example.com/video.mp4",
"contentType": "landscape",
"publish": true
}
}
}'const response = await fetch('https://api.rad.live/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
query: `
mutation($input: PublishVideoInput!) {
publishVideo(input: $input) {
content { id metadata { title } }
job { id status }
}
}
`,
variables: {
input: {
title: 'My Video',
summary: 'A great video about something.',
sourceUrl: 'https://example.com/video.mp4',
contentType: 'landscape',
publish: true,
},
},
}),
});
const { data } = await response.json();
console.log(data.publishVideo.content.id);import requests
response = requests.post(
"https://api.rad.live/graphql",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
},
json={
"query": """
mutation($input: PublishVideoInput!) {
publishVideo(input: $input) {
content { id metadata { title } }
job { id status }
}
}
""",
"variables": {
"input": {
"title": "My Video",
"summary": "A great video about something.",
"sourceUrl": "https://example.com/video.mp4",
"contentType": "landscape",
"publish": True,
}
},
},
)
result = response.json()["data"]["publishVideo"]
print(result["content"]["id"])Mode 2: Local File Upload
Provide filename and size instead of sourceUrl. The server creates an ingestion session and returns an upload endpoint.
# Step 1: Create the ingestion session
curl -X POST https://api.rad.live/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"query": "mutation($input: PublishVideoInput!) { publishVideo(input: $input) { content { id } upload { endpoint } } }",
"variables": {
"input": {
"title": "My Local Video",
"filename": "my-video.mp4",
"size": 524288000,
"contentType": "landscape",
"publish": true
}
}
}'
# Step 2: Upload the file to the returned endpoint
curl -X POST <UPLOAD_ENDPOINT> \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@my-video.mp4"// Step 1: Create ingestion session
const res = await fetch('https://api.rad.live/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
query: `
mutation($input: PublishVideoInput!) {
publishVideo(input: $input) {
content { id }
upload { endpoint }
}
}
`,
variables: {
input: {
title: 'My Local Video',
filename: 'my-video.mp4',
size: 524288000,
contentType: 'landscape',
publish: true,
},
},
}),
});
const { data } = await res.json();
const uploadEndpoint = data.publishVideo.upload.endpoint;
// Step 2: Upload the file
const formData = new FormData();
formData.append('file', videoFile);
await fetch(uploadEndpoint, {
method: 'POST',
headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
body: formData,
});
// Auto-finalize: server automatically transcodes and publishes after upload completesimport requests
# Step 1: Create ingestion session
response = requests.post(
"https://api.rad.live/graphql",
headers={
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
},
json={
"query": """
mutation($input: PublishVideoInput!) {
publishVideo(input: $input) {
content { id }
upload { endpoint }
}
}
""",
"variables": {
"input": {
"title": "My Local Video",
"filename": "my-video.mp4",
"size": 524288000,
"contentType": "landscape",
"publish": True,
}
},
},
)
result = response.json()["data"]["publishVideo"]
upload_endpoint = result["upload"]["endpoint"]
# Step 2: Upload the file
with open("my-video.mp4", "rb") as f:
requests.post(
upload_endpoint,
headers={"Authorization": "Bearer YOUR_API_KEY"},
files={"file": f},
)
# Auto-finalize: server transcodes and publishes after uploadAuto-finalize: In Mode 2, the server stores your workflow parameters (layout, enhance, publish) and automatically triggers transcoding and publication after the upload completes. No additional API calls needed.
PublishVideoInput Fields
| Field | Type | Required | Description |
|---|---|---|---|
title | String | Yes | Content title |
summary | String | No | Content description |
channel | DID | No | Channel DID (defaults to your channel) |
sourceUrl | URL | Mode 1 | URL to download video from |
filename | String | Mode 2 | Local file name |
size | Int | Mode 2 | File size in bytes |
contentType | VideoContentType | No | landscape, portrait, vr, or short |
layout | ContentLayout | No | LANDSCAPE, PORTRAIT, or VR |
enhance | Boolean | No | Enable AI upscaling + audio upmixing (Creator+) |
publish | Boolean | No | Publish immediately after transcoding |
releaseDate | String | No | Scheduled release date (ISO 8601) |
thumbnailUrl | URL | No | Custom thumbnail URL |
Option 2: Step-by-Step
For full control, use individual mutations:
Create content
mutation {
createContent(input: {
metadata: { title: "My Video", summary: "Description" }
hints: { type: FEATURE }
}) {
id
}
}Upload a video asset
mutation {
createContentAsset(
id: "did:rad.live:content/feature/123"
input: { filename: "video.mp4", size: 524288000 }
) {
id
endpoint
}
}Then upload the file to the returned TUS endpoint. See Uploads for details.
Submit for transcoding
mutation {
submitContentForProcessing(
id: "did:rad.live:content/feature/123"
input: { assetName: "video.mp4", layout: LANDSCAPE, enhance: false }
) {
job { id status }
}
}Publish
mutation {
publishContent(id: "did:rad.live:content/feature/123") {
id
metadata { title }
}
}Checking Processing Status
After submitting for processing, you can poll the transcode job or use waitForProcessing:
mutation {
waitForProcessing(input: {
contentId: "did:rad.live:content/feature/123"
timeoutSeconds: 300
}) {
status
job { status progress }
}
}The waitForProcessing mutation blocks server-side until the job completes, errors, or times out — eliminating the need for client-side polling loops.
Next steps
- AI Upscaling — Add
enhance: truefor 4K/8K upscaling - Output Assets — See what you get after transcoding
- Uploads — Deep dive into the TUS upload protocol