curl --request POST \
--url https://api.hooked.so/v1/project/create/podcast-interview \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data @- <<EOF
{
"script": "[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\n[B] Quick question. Do you consider yourself middle class? [/B]\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\n[B] If you want to learn to save, comment SAVE below. [/B]",
"avatarId": "avatar_podcast_host_01",
"guest": {
"source": "generated",
"description": "a man in his 30s, casual, average looking"
},
"caption": {
"preset": "tiktok",
"alignment": "bottom",
"disabled": false
},
"webhook": "https://yoursite.com/webhook"
}
EOFimport requests
url = "https://api.hooked.so/v1/project/create/podcast-interview"
payload = {
"script": "[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]
[B] Quick question. Do you consider yourself middle class? [/B]
[A] [pauses, thinking] Hmm, I'd say yes. [/A]
[B] If you want to learn to save, comment SAVE below. [/B]",
"avatarId": "avatar_podcast_host_01",
"guest": {
"source": "generated",
"description": "a man in his 30s, casual, average looking"
},
"caption": {
"preset": "tiktok",
"alignment": "bottom",
"disabled": False
},
"webhook": "https://yoursite.com/webhook"
}
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({
script: '[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\n[B] Quick question. Do you consider yourself middle class? [/B]\n[A] [pauses, thinking] Hmm, I\'d say yes. [/A]\n[B] If you want to learn to save, comment SAVE below. [/B]',
avatarId: 'avatar_podcast_host_01',
guest: {source: 'generated', description: 'a man in his 30s, casual, average looking'},
caption: {preset: 'tiktok', alignment: 'bottom', disabled: false},
webhook: 'https://yoursite.com/webhook'
})
};
fetch('https://api.hooked.so/v1/project/create/podcast-interview', 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/podcast-interview",
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([
'script' => '[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]
[B] Quick question. Do you consider yourself middle class? [/B]
[A] [pauses, thinking] Hmm, I\'d say yes. [/A]
[B] If you want to learn to save, comment SAVE below. [/B]',
'avatarId' => 'avatar_podcast_host_01',
'guest' => [
'source' => 'generated',
'description' => 'a man in his 30s, casual, average looking'
],
'caption' => [
'preset' => 'tiktok',
'alignment' => 'bottom',
'disabled' => false
],
'webhook' => 'https://yoursite.com/webhook'
]),
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/podcast-interview"
payload := strings.NewReader("{\n \"script\": \"[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\\n[B] Quick question. Do you consider yourself middle class? [/B]\\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\\n[B] If you want to learn to save, comment SAVE below. [/B]\",\n \"avatarId\": \"avatar_podcast_host_01\",\n \"guest\": {\n \"source\": \"generated\",\n \"description\": \"a man in his 30s, casual, average looking\"\n },\n \"caption\": {\n \"preset\": \"tiktok\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"webhook\": \"https://yoursite.com/webhook\"\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/podcast-interview")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"script\": \"[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\\n[B] Quick question. Do you consider yourself middle class? [/B]\\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\\n[B] If you want to learn to save, comment SAVE below. [/B]\",\n \"avatarId\": \"avatar_podcast_host_01\",\n \"guest\": {\n \"source\": \"generated\",\n \"description\": \"a man in his 30s, casual, average looking\"\n },\n \"caption\": {\n \"preset\": \"tiktok\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"webhook\": \"https://yoursite.com/webhook\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hooked.so/v1/project/create/podcast-interview")
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 \"script\": \"[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\\n[B] Quick question. Do you consider yourself middle class? [/B]\\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\\n[B] If you want to learn to save, comment SAVE below. [/B]\",\n \"avatarId\": \"avatar_podcast_host_01\",\n \"guest\": {\n \"source\": \"generated\",\n \"description\": \"a man in his 30s, casual, average looking\"\n },\n \"caption\": {\n \"preset\": \"tiktok\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"webhook\": \"https://yoursite.com/webhook\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"videoId": "vid_talking_avatar_abc123xyz",
"projectId": "proj_ugc_abc123xyz",
"status": "STARTED"
},
"message": "Talking avatar successfully created"
}{
"success": false,
"message": "script: Script must be at least 1 character"
}{
"success": false,
"message": "Invalid API key"
}Podcast
Create a podcast interview with two AI avatars
curl --request POST \
--url https://api.hooked.so/v1/project/create/podcast-interview \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data @- <<EOF
{
"script": "[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\n[B] Quick question. Do you consider yourself middle class? [/B]\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\n[B] If you want to learn to save, comment SAVE below. [/B]",
"avatarId": "avatar_podcast_host_01",
"guest": {
"source": "generated",
"description": "a man in his 30s, casual, average looking"
},
"caption": {
"preset": "tiktok",
"alignment": "bottom",
"disabled": false
},
"webhook": "https://yoursite.com/webhook"
}
EOFimport requests
url = "https://api.hooked.so/v1/project/create/podcast-interview"
payload = {
"script": "[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]
[B] Quick question. Do you consider yourself middle class? [/B]
[A] [pauses, thinking] Hmm, I'd say yes. [/A]
[B] If you want to learn to save, comment SAVE below. [/B]",
"avatarId": "avatar_podcast_host_01",
"guest": {
"source": "generated",
"description": "a man in his 30s, casual, average looking"
},
"caption": {
"preset": "tiktok",
"alignment": "bottom",
"disabled": False
},
"webhook": "https://yoursite.com/webhook"
}
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({
script: '[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\n[B] Quick question. Do you consider yourself middle class? [/B]\n[A] [pauses, thinking] Hmm, I\'d say yes. [/A]\n[B] If you want to learn to save, comment SAVE below. [/B]',
avatarId: 'avatar_podcast_host_01',
guest: {source: 'generated', description: 'a man in his 30s, casual, average looking'},
caption: {preset: 'tiktok', alignment: 'bottom', disabled: false},
webhook: 'https://yoursite.com/webhook'
})
};
fetch('https://api.hooked.so/v1/project/create/podcast-interview', 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/podcast-interview",
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([
'script' => '[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]
[B] Quick question. Do you consider yourself middle class? [/B]
[A] [pauses, thinking] Hmm, I\'d say yes. [/A]
[B] If you want to learn to save, comment SAVE below. [/B]',
'avatarId' => 'avatar_podcast_host_01',
'guest' => [
'source' => 'generated',
'description' => 'a man in his 30s, casual, average looking'
],
'caption' => [
'preset' => 'tiktok',
'alignment' => 'bottom',
'disabled' => false
],
'webhook' => 'https://yoursite.com/webhook'
]),
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/podcast-interview"
payload := strings.NewReader("{\n \"script\": \"[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\\n[B] Quick question. Do you consider yourself middle class? [/B]\\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\\n[B] If you want to learn to save, comment SAVE below. [/B]\",\n \"avatarId\": \"avatar_podcast_host_01\",\n \"guest\": {\n \"source\": \"generated\",\n \"description\": \"a man in his 30s, casual, average looking\"\n },\n \"caption\": {\n \"preset\": \"tiktok\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"webhook\": \"https://yoursite.com/webhook\"\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/podcast-interview")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"script\": \"[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\\n[B] Quick question. Do you consider yourself middle class? [/B]\\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\\n[B] If you want to learn to save, comment SAVE below. [/B]\",\n \"avatarId\": \"avatar_podcast_host_01\",\n \"guest\": {\n \"source\": \"generated\",\n \"description\": \"a man in his 30s, casual, average looking\"\n },\n \"caption\": {\n \"preset\": \"tiktok\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"webhook\": \"https://yoursite.com/webhook\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hooked.so/v1/project/create/podcast-interview")
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 \"script\": \"[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\\n[B] Quick question. Do you consider yourself middle class? [/B]\\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\\n[B] If you want to learn to save, comment SAVE below. [/B]\",\n \"avatarId\": \"avatar_podcast_host_01\",\n \"guest\": {\n \"source\": \"generated\",\n \"description\": \"a man in his 30s, casual, average looking\"\n },\n \"caption\": {\n \"preset\": \"tiktok\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"webhook\": \"https://yoursite.com/webhook\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"videoId": "vid_talking_avatar_abc123xyz",
"projectId": "proj_ugc_abc123xyz",
"status": "STARTED"
},
"message": "Talking avatar successfully created"
}{
"success": false,
"message": "script: Script must be at least 1 character"
}{
"success": false,
"message": "Invalid API key"
}Overview
Two AI avatars talking, in one of two shapes:- Podcast: each person filmed on their own and cut back and forth, like a real podcast.
- Dualcast: both people in the same shot; in each clip one talks while the other listens and reacts.
- Educational and opinion clips
- Product mentions in a natural conversation
- Q&A and “one question” formats
Endpoint
POST /v1/project/create/podcast-interview
Required Fields
podcast (needs avatarId and guest) or dualcast (needs dualcastImageKey, dualcastAvatarId, or dualcastAvatars to make the image).[A] … [/A] (the host, or the person on the left in dualcast) and [B] … [/B] (the guest, or the person on the right). Lines starting with A: / B: are read the same. Both people must speak. A long turn is split between sentences into clips of up to 10 seconds, up to 24 clips per video.Inside a turn:- an action in brackets, in your words:
[nods]at the start happens before speaking, in the middle while saying the words around it, at the end after the line [camera] … [/camera]marks the words said to the camera, to the audience, instead of to the other one (the closing CTA, for instance)[product] … [/product]marks the words while the product photo (productImageKey) is on screen, and whoever is talking points up to it as it appears (unless you wrote an action right there)
/v1/avatar/list. An avatar on a podcast set works best: a generated guest sits in the same room.guest.voice is read, as the voice of the person on the right.Show Guest Object
Show Guest Object
generated: a new person in the same podcast room, seen from the other seatavatar: another avatar, seated in the same podcast room from the other seat
source is generated (max 300 characters): who the guest is, e.g. a man in his 30s, casual, average looking.source is avatar: an avatar ID from /v1/avatar/list.young-woman-25-30, woman-mid-20s, woman-mid-30s, older-woman-50, young-man-25-30, man-mid-20s, man-mid-30s or older-man-50. Unset: the one that fits the guest.dualcastImageKey: an avatar ID from /v1/avatar/list whose image already shows both people (made in the avatar editor with “Both people in one shot”). A is the person on the left, B the one on the right. It must show exactly two people.dualcastImageKey: two avatar IDs (left, right) from /v1/avatar/list. They are put side by side on a podcast set, each with their microphone (one more image): left is A, right is B.dualcastAvatars, optional: the set they sit on, in your words (max 300 characters), e.g. a modern studio with a wooden slat wall.Optional Fields
a cozy podcast studio with a boom microphone. Only the background changes, and the guest sits in the same room. Useful when the avatar is not on a podcast set.[product] … [/product].guest.voice. Unset: the one that fits the host (podcast), or the person on the left of the image (dualcast).us-neutral-general-american, spanish-mexico). Unset: the language’s neutral accent.en, es). Unset: detected from the script./v1/music/list for background music.preset, alignment, disabled).Request Example
{
"script": "[A] 8 out of 10 people say they struggle to make it to the end of the month. [/A]\n[B] Quick question. Do you consider yourself middle class? [/B]\n[A] [pauses, thinking] Hmm, I'd say yes. [/A]\n[B] [camera] If you want to learn to save, comment SAVE below. [/camera] [/B]",
"avatarId": "avatar_podcast_host_01",
"guest": {
"source": "generated",
"description": "a man in his 30s, casual, average looking"
},
"caption": { "preset": "tiktok", "alignment": "bottom", "disabled": false },
"webhook": "https://yoursite.com/webhook"
}
Dualcast Example
{
"mode": "dualcast",
"script": "[A] I look at myself in the mirror in these leggings and go: damn, I look good. [she bursts out laughing] [/A]\n[B] They lift you and cinch your waist, it's next level. [/B]\n[A] I'm leaving a photo up here [product] because otherwise you won't get it. [/product] [/A]\n[B] The link is down below, go take a look. [they both wave goodbye] [/B]",
"dualcastImageKey": "https://yoursite.com/two-hosts.jpg",
"hostVoice": "woman-mid-20s",
"guest": { "source": "generated", "voice": "young-woman-25-30" }
}
Authorizations
Body
The dialogue, each turn wrapped in who says it: [A] … [/A] (the host, or the person on the left in dualcast) and [B] … [/B] (the guest, or the person on the right). Lines starting with "A:" / "B:" are read the same. An action in brackets ("[nods]") at the start of a turn happens before speaking, in the middle while saying the words around it, at the end after the line. [camera] … [/camera] marks the words said to the camera, to the audience. [product] … [/product] marks the words while the product photo is on screen; whoever is talking points up to it as it appears. Both people must speak; up to 24 clips.
1 - 10000podcast: each person filmed on their own, cut back and forth (needs avatarId and guest). dualcast: both people in one image (needs dualcastImageKey, dualcastAvatarId, or dualcastAvatars to make it).
podcast, dualcast Video name (max 100 characters)
100Podcast mode: the host (A), an avatar ID from /v1/avatar/list. A podcast set works best, since a generated guest sits in the same room.
30Podcast mode: the guest (B). In dualcast only guest.voice is read: the voice of the person on the right.
Show child attributes
Show child attributes
Dualcast only: the image with both people, as a storage key from your media library or an https URL. A is the person on the left, B the one on the right.
1000Dualcast mode, instead of dualcastImageKey: an avatar ID from /v1/avatar/list whose image already shows both people (made in the avatar editor with "Both people in one shot"). A is the person on the left, B the one on the right. It must show exactly two people.
30Dualcast mode, instead of dualcastImageKey: two avatar IDs from /v1/avatar/list, put side by side on a podcast set (one more image). left is A, right is B.
Show child attributes
Show child attributes
With dualcastAvatars, optional: the set they sit on, in your words (e.g. "a modern studio with a wooden slat wall").
300Podcast mode, optional: move the host to this set first, in your words (e.g. "a cozy podcast studio with a boom microphone"). Only the background changes; the guest sits in the same room.
300Optional: a product photo (storage key from your media library or an https URL), shown at the top of the video on the words marked [product] … [/product].
1000The host's voice. Unset: the one that fits the avatar.
young-woman-25-30, woman-mid-20s, woman-mid-30s, older-woman-50, young-man-25-30, man-mid-20s, man-mid-30s, older-man-50 One accent for both, from the English and Spanish accents (e.g. us-neutral-general-american, spanish-mexico). Unset: the language's neutral one.
The conversation's language (e.g. en, es). Unset: detected from the script.
Music ID from /v1/music/list for background music
30Show child attributes
Show child attributes
HTTPS URL to receive completion notification
500Custom metadata object (max 5KB)