Create Hook + Demo Video
curl --request POST \
--url https://api.hooked.so/v1/project/create/hook-demo \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"avatarId": "6",
"name": "Product Launch Hook",
"text": "Wait for it... This changed EVERYTHING",
"textSettings": {
"preset": "modern",
"alignment": "middle",
"textColor": "#FFFFFF",
"backgroundColor": "transparent",
"disabled": false
},
"media": [],
"aspectRatio": "ratio_9_16",
"webhook": "https://yoursite.com/webhook",
"musicId": "your-custom-music-id",
"metadata": {
"campaignId": "campaign_id",
"variant": "A",
"createdBy": "created_by"
}
}
'import requests
url = "https://api.hooked.so/v1/project/create/hook-demo"
payload = {
"avatarId": "6",
"name": "Product Launch Hook",
"text": "Wait for it... This changed EVERYTHING",
"textSettings": {
"preset": "modern",
"alignment": "middle",
"textColor": "#FFFFFF",
"backgroundColor": "transparent",
"disabled": False
},
"media": [],
"aspectRatio": "ratio_9_16",
"webhook": "https://yoursite.com/webhook",
"musicId": "your-custom-music-id",
"metadata": {
"campaignId": "campaign_id",
"variant": "A",
"createdBy": "created_by"
}
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
avatarId: '6',
name: 'Product Launch Hook',
text: 'Wait for it... This changed EVERYTHING',
textSettings: {
preset: 'modern',
alignment: 'middle',
textColor: '#FFFFFF',
backgroundColor: 'transparent',
disabled: false
},
media: [],
aspectRatio: 'ratio_9_16',
webhook: 'https://yoursite.com/webhook',
musicId: 'your-custom-music-id',
metadata: {campaignId: 'campaign_id', variant: 'A', createdBy: 'created_by'}
})
};
fetch('https://api.hooked.so/v1/project/create/hook-demo', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hooked.so/v1/project/create/hook-demo",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'avatarId' => '6',
'name' => 'Product Launch Hook',
'text' => 'Wait for it... This changed EVERYTHING',
'textSettings' => [
'preset' => 'modern',
'alignment' => 'middle',
'textColor' => '#FFFFFF',
'backgroundColor' => 'transparent',
'disabled' => false
],
'media' => [
],
'aspectRatio' => 'ratio_9_16',
'webhook' => 'https://yoursite.com/webhook',
'musicId' => 'your-custom-music-id',
'metadata' => [
'campaignId' => 'campaign_id',
'variant' => 'A',
'createdBy' => 'created_by'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hooked.so/v1/project/create/hook-demo"
payload := strings.NewReader("{\n \"avatarId\": \"6\",\n \"name\": \"Product Launch Hook\",\n \"text\": \"Wait for it... This changed EVERYTHING\",\n \"textSettings\": {\n \"preset\": \"modern\",\n \"alignment\": \"middle\",\n \"textColor\": \"#FFFFFF\",\n \"backgroundColor\": \"transparent\",\n \"disabled\": false\n },\n \"media\": [],\n \"aspectRatio\": \"ratio_9_16\",\n \"webhook\": \"https://yoursite.com/webhook\",\n \"musicId\": \"your-custom-music-id\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\",\n \"createdBy\": \"created_by\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hooked.so/v1/project/create/hook-demo")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"avatarId\": \"6\",\n \"name\": \"Product Launch Hook\",\n \"text\": \"Wait for it... This changed EVERYTHING\",\n \"textSettings\": {\n \"preset\": \"modern\",\n \"alignment\": \"middle\",\n \"textColor\": \"#FFFFFF\",\n \"backgroundColor\": \"transparent\",\n \"disabled\": false\n },\n \"media\": [],\n \"aspectRatio\": \"ratio_9_16\",\n \"webhook\": \"https://yoursite.com/webhook\",\n \"musicId\": \"your-custom-music-id\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\",\n \"createdBy\": \"created_by\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hooked.so/v1/project/create/hook-demo")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"avatarId\": \"6\",\n \"name\": \"Product Launch Hook\",\n \"text\": \"Wait for it... This changed EVERYTHING\",\n \"textSettings\": {\n \"preset\": \"modern\",\n \"alignment\": \"middle\",\n \"textColor\": \"#FFFFFF\",\n \"backgroundColor\": \"transparent\",\n \"disabled\": false\n },\n \"media\": [],\n \"aspectRatio\": \"ratio_9_16\",\n \"webhook\": \"https://yoursite.com/webhook\",\n \"musicId\": \"your-custom-music-id\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\",\n \"createdBy\": \"created_by\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"videoId": "vid_ugc_abc123xyz",
"projectId": "proj_hook_abc123xyz",
"status": "COMPLETED"
},
"message": "Hook Demo successfully created"
}
{
"success": false,
"message": "text: Text is required"
}
{
"success": false,
"message": "webhook: Must be a valid HTTPS URL"
}
Videos
Hook + Demo
Create engaging Hook + Demo videos with text overlays and background media
POST
/
v1
/
project
/
create
/
hook-demo
Create Hook + Demo Video
curl --request POST \
--url https://api.hooked.so/v1/project/create/hook-demo \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"avatarId": "6",
"name": "Product Launch Hook",
"text": "Wait for it... This changed EVERYTHING",
"textSettings": {
"preset": "modern",
"alignment": "middle",
"textColor": "#FFFFFF",
"backgroundColor": "transparent",
"disabled": false
},
"media": [],
"aspectRatio": "ratio_9_16",
"webhook": "https://yoursite.com/webhook",
"musicId": "your-custom-music-id",
"metadata": {
"campaignId": "campaign_id",
"variant": "A",
"createdBy": "created_by"
}
}
'import requests
url = "https://api.hooked.so/v1/project/create/hook-demo"
payload = {
"avatarId": "6",
"name": "Product Launch Hook",
"text": "Wait for it... This changed EVERYTHING",
"textSettings": {
"preset": "modern",
"alignment": "middle",
"textColor": "#FFFFFF",
"backgroundColor": "transparent",
"disabled": False
},
"media": [],
"aspectRatio": "ratio_9_16",
"webhook": "https://yoursite.com/webhook",
"musicId": "your-custom-music-id",
"metadata": {
"campaignId": "campaign_id",
"variant": "A",
"createdBy": "created_by"
}
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
avatarId: '6',
name: 'Product Launch Hook',
text: 'Wait for it... This changed EVERYTHING',
textSettings: {
preset: 'modern',
alignment: 'middle',
textColor: '#FFFFFF',
backgroundColor: 'transparent',
disabled: false
},
media: [],
aspectRatio: 'ratio_9_16',
webhook: 'https://yoursite.com/webhook',
musicId: 'your-custom-music-id',
metadata: {campaignId: 'campaign_id', variant: 'A', createdBy: 'created_by'}
})
};
fetch('https://api.hooked.so/v1/project/create/hook-demo', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hooked.so/v1/project/create/hook-demo",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'avatarId' => '6',
'name' => 'Product Launch Hook',
'text' => 'Wait for it... This changed EVERYTHING',
'textSettings' => [
'preset' => 'modern',
'alignment' => 'middle',
'textColor' => '#FFFFFF',
'backgroundColor' => 'transparent',
'disabled' => false
],
'media' => [
],
'aspectRatio' => 'ratio_9_16',
'webhook' => 'https://yoursite.com/webhook',
'musicId' => 'your-custom-music-id',
'metadata' => [
'campaignId' => 'campaign_id',
'variant' => 'A',
'createdBy' => 'created_by'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hooked.so/v1/project/create/hook-demo"
payload := strings.NewReader("{\n \"avatarId\": \"6\",\n \"name\": \"Product Launch Hook\",\n \"text\": \"Wait for it... This changed EVERYTHING\",\n \"textSettings\": {\n \"preset\": \"modern\",\n \"alignment\": \"middle\",\n \"textColor\": \"#FFFFFF\",\n \"backgroundColor\": \"transparent\",\n \"disabled\": false\n },\n \"media\": [],\n \"aspectRatio\": \"ratio_9_16\",\n \"webhook\": \"https://yoursite.com/webhook\",\n \"musicId\": \"your-custom-music-id\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\",\n \"createdBy\": \"created_by\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hooked.so/v1/project/create/hook-demo")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"avatarId\": \"6\",\n \"name\": \"Product Launch Hook\",\n \"text\": \"Wait for it... This changed EVERYTHING\",\n \"textSettings\": {\n \"preset\": \"modern\",\n \"alignment\": \"middle\",\n \"textColor\": \"#FFFFFF\",\n \"backgroundColor\": \"transparent\",\n \"disabled\": false\n },\n \"media\": [],\n \"aspectRatio\": \"ratio_9_16\",\n \"webhook\": \"https://yoursite.com/webhook\",\n \"musicId\": \"your-custom-music-id\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\",\n \"createdBy\": \"created_by\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hooked.so/v1/project/create/hook-demo")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"avatarId\": \"6\",\n \"name\": \"Product Launch Hook\",\n \"text\": \"Wait for it... This changed EVERYTHING\",\n \"textSettings\": {\n \"preset\": \"modern\",\n \"alignment\": \"middle\",\n \"textColor\": \"#FFFFFF\",\n \"backgroundColor\": \"transparent\",\n \"disabled\": false\n },\n \"media\": [],\n \"aspectRatio\": \"ratio_9_16\",\n \"webhook\": \"https://yoursite.com/webhook\",\n \"musicId\": \"your-custom-music-id\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\",\n \"createdBy\": \"created_by\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"videoId": "vid_ugc_abc123xyz",
"projectId": "proj_hook_abc123xyz",
"status": "COMPLETED"
},
"message": "Hook Demo successfully created"
}
{
"success": false,
"message": "text: Text is required"
}
{
"success": false,
"message": "webhook: Must be a valid HTTPS URL"
}
Try it out! Use the API playground on the right to test the Hook + Demo endpoint directly. The schema is pre-configured for
hook_demo type.Overview
Hook + Demo videos are perfect for creating attention-grabbing content with text hooks displayed over demo media (images or videos). This format is ideal for:- Product demonstrations with compelling hooks
- Social media content with text overlays
- Marketing videos with call-to-action text
- Quick promotional clips
Hook + Demo videos are generated in real-time, making them one of the fastest video types to create.
Endpoint
POST /v1/project/create/hook-demo
The endpoint is the same for all video types. Set
type: "hook_demo" to create a Hook + Demo video.Required Fields
string
required
Avatar template ID from
/v1/avatar/list. This is the avatar that will appear in your Hook + Demo video.string
required
The hook text to display on the video (1-5,000 characters). This is the main text that will appear as an overlay on your media.
Optional Fields
string
Video name (max 100 characters)
array
Background media IDs (max 50). Array of media ID strings. These are the images or videos that will play behind your text hook.
string
Music template ID from
/v1/music/list or custom music ID.object
Text display settings for the hook overlay
Show Text Settings Object
Show Text Settings Object
string
default:"tiktok"
Text preset style. Available presets include:
modern, tiktok, social, impact, slicedText, minimalMonochrome, highlight, gradient, neonFuture, avantGarde, glassmorphic, stageEffect, neonEffect, glowEffect, wrapEffectstring
default:"bottom"
Text position on the video:
top, middle, or bottomstring
Custom text color in hex format (e.g.,
#ffffff). Max 100 characters.string
Custom background color for the text box in hex format (e.g.,
#000000). Max 100 characters.boolean
default:"false"
Set to
true to hide the text overlay (useful when you only want the media)string
default:"ratio_9_16"
Video aspect ratio:
ratio_9_16: Vertical (TikTok, Reels, Shorts) - Recommendedratio_16_9: Horizontal (YouTube)ratio_1_1: Square (Instagram)
string
HTTPS URL to receive completion notification (max 500 characters). Highly recommended for production use.
object
Custom metadata object (max 5KB). Store any additional data you need to associate with this video.
{
"campaignId": "summer2024",
"productSku": "SKU-12345",
"customField": "any value"
}
Request Examples
Basic Hook + Demo
{
"avatarId": "6",
"name": "Product Launch Hook",
"text": "This changed everything..."
}
With Background Media
{
"avatarId": 6,
"name": "Product Demo Video",
"musicId": "music_id",
"text": "Wait for it... This product will blow your mind!",
"textSettings": {
"preset": "impact",
"alignment": "bottom",
"disabled": false
},
"media": ["media_001", "media_002"],
"aspectRatio": "ratio_9_16",
"webhook": "https://yoursite.com/webhooks/video-complete"
}
With Custom Colors and Metadata
{
"avatarId": "6",
"name": "Brand Campaign Hook",
"text": "3 secrets they don't want you to know...",
"textSettings": {
"preset": "gradient",
"alignment": "top",
"textColor": "#00ff88",
"backgroundColor": "transparent",
"disabled": false
},
"media": ["media_bg"],
"aspectRatio": "ratio_9_16",
"webhook": "https://api.yoursite.com/hooks/hooked",
"metadata": {
"campaignId": "Q1-2024-hooks",
"abTestVariant": "A",
"internalRef": "MKT-1234"
}
}
Response
{
"success": true,
"data": {
"videoId": "vid_ugc_abc123xyz",
"projectId": "proj_hook_abc123xyz",
"status": "COMPLETED"
},
"message": "Hook Demo successfully created"
}
{
"success": false,
"message": "text: Text is required"
}
{
"success": false,
"message": "webhook: Must be a valid HTTPS URL"
}
Webhook Notification
When your video is ready, we’ll POST to your webhook URL:Webhook Payload
{
"status": "COMPLETED",
"data": {
"videoId": "video_xyz789",
"status": "COMPLETED",
"url": "https://cdn.hooked.so/videos/xyz789.mp4",
"shareUrl": "https://cdn.hooked.so/shared/xyz789.mp4",
"metadata": {
"projectId": "proj_hook_abc123xyz",
"campaignId": "Q1-2024-hooks",
"abTestVariant": "A"
},
},
"message": "Video completed",
}
Your webhook endpoint must return a
200 status code. We’ll retry up to 3 times if the request fails.Text Presets
Available text presets for thetextSettings.preset field:
| Preset | Description |
|---|---|
modern | Clean, contemporary style with subtle shadows (Default) |
tiktok | Viral & trendy style with bold outlines, perfect for social media |
social | White background with black text, great for readability |
impact | Bold & commanding style with uppercase text and strong shadows |
slicedText | Edgy & modern with colorful shadow effects (Pro) |
minimalMonochrome | Ultra minimal style with lowercase text and subtle borders |
highlight | Modern & distinctive with colored background boxes |
gradient | Dynamic & modern with gradient text colors (Pro) |
neonFuture | Electric & bold with cyan neon glow effect |
avantGarde | Fashion forward with split gradient text (Pro) |
glassmorphic | Modern transparency effect with glass-like styling |
stageEffect | Gradient stage effect with colorful text |
neonEffect | Electric glow with magenta neon shadows |
glowEffect | Soft white glow effect for elegant styling |
wrapEffect | Wrapped style with gradient text and blur backdrop |
You can override the preset colors using
textColor and backgroundColor while keeping the preset’s font styling and animations.Best Practices
Keep Hooks Short
Effective hooks are 5-15 words. Get attention quickly.
Use High-Quality Media
Upload media at least 1080p for best results. MP4 for videos, JPG/PNG for images.
Match Aspect Ratio
Ensure your media matches your target aspect ratio to avoid cropping.
Use Webhooks
Always use webhooks in production instead of polling for video status.
Common Use Cases
Social Media Hooks
Social Media Hooks
Create attention-grabbing hooks for TikTok, Instagram Reels, or YouTube Shorts.
Product Demos
Product Demos
Showcase products with compelling text overlays.
{
"avatarId": "6",
"name": "Product Feature",
"text": "Watch what happens when you press this button...",
"textSettings": {
"preset": "modern",
"alignment": "bottom",
"textColor": "#00ff88",
"backgroundColor": "transparent",
"disabled": false
},
"media": ["demo_vid"]
}
Testimonial Hooks
Testimonial Hooks
Highlight customer quotes or testimonials.
{
"avatarId": "6",
"name": "Customer Quote",
"text": "\"This saved me 10 hours every week\" - Sarah M.",
"textSettings": {
"preset": "highlight",
"alignment": "middle",
"textColor": "#ffffff",
"backgroundColor": "#1a1a1a"
}
}
Error Handling
| Error | Description | Solution |
|---|---|---|
text: Text is required | Missing hook text | Add the text field with your hook content |
webhook: Must be a valid HTTPS URL | Invalid webhook URL | Ensure webhook URL starts with https:// |
media: Cannot have more than 50 media items | Too many media items | Reduce media array to 50 items or fewer |
textSettings.alignment: Invalid alignment value | Invalid alignment | Use top, middle, or bottom |
Next Steps
List Videos
View all your created videos
Get Video Details
Check video status and download URL
Webhooks Guide
Learn how to handle webhook notifications
Best Practices
Tips for creating effective videos
Authorizations
Body
application/json
Avatar template ID from /v1/avatar/list
The hook text to display on the video (1-5,000 characters)
Required string length:
1 - 5000Video name (max 100 characters)
Maximum string length:
100Text settings object
Show child attributes
Show child attributes
Background media items (max 50)
Maximum array length:
50Unique media ID
Video aspect ratio
Available options:
ratio_9_16, ratio_16_9, ratio_1_1 Music template ID from /v1/music/list or custom music ID.
HTTPS URL to receive completion notification
Maximum string length:
500Custom metadata object (max 5KB)