curl --request POST \
--url https://app.demand-iq.com/api/decks/{deckId}/presentations \
--header 'Content-Type: application/json' \
--cookie session= \
--data '
{
"prospect_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"voice_settings": {},
"language_settings": {},
"price_inputs": {},
"measurement_strategy": "manual"
}
'import requests
url = "https://app.demand-iq.com/api/decks/{deckId}/presentations"
payload = {
"prospect_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"voice_settings": {},
"language_settings": {},
"price_inputs": {},
"measurement_strategy": "manual"
}
headers = {
"cookie": "session=",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {cookie: 'session=', 'Content-Type': 'application/json'},
body: JSON.stringify({
prospect_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
voice_settings: {},
language_settings: {},
price_inputs: {},
measurement_strategy: 'manual'
})
};
fetch('https://app.demand-iq.com/api/decks/{deckId}/presentations', 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://app.demand-iq.com/api/decks/{deckId}/presentations",
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([
'prospect_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'voice_settings' => [
],
'language_settings' => [
],
'price_inputs' => [
],
'measurement_strategy' => 'manual'
]),
CURLOPT_COOKIE => "session=",
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$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://app.demand-iq.com/api/decks/{deckId}/presentations"
payload := strings.NewReader("{\n \"prospect_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"voice_settings\": {},\n \"language_settings\": {},\n \"price_inputs\": {},\n \"measurement_strategy\": \"manual\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("cookie", "session=")
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://app.demand-iq.com/api/decks/{deckId}/presentations")
.header("cookie", "session=")
.header("Content-Type", "application/json")
.body("{\n \"prospect_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"voice_settings\": {},\n \"language_settings\": {},\n \"price_inputs\": {},\n \"measurement_strategy\": \"manual\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.demand-iq.com/api/decks/{deckId}/presentations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["cookie"] = 'session='
request["Content-Type"] = 'application/json'
request.body = "{\n \"prospect_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"voice_settings\": {},\n \"language_settings\": {},\n \"price_inputs\": {},\n \"measurement_strategy\": \"manual\"\n}"
response = http.request(request)
puts response.read_body{
"deck_presentation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"url": "<string>",
"status": "not_started"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>",
"code": "DECK_GENERATING"
}{
"error": "<string>",
"code": "INVALID_ADDRESS"
}{
"error": "<string>"
}{
"error": "<string>"
}Create a presentation
Creates a personalized presentation. Provide homeowner, prospect_id, or both — at least one is required. With only homeowner, a new prospect is created from it. With only prospect_id, homeowner data is fetched from that prospect’s journey. With both, the supplied homeowner data is used as-is and the existing prospect is attached (it must be visible to your credential — otherwise 422 PROSPECT_NOT_FOUND). The address is validated via Google Geocoding. Returns a shareable URL immediately; audio generation may continue in the background.
Price inputs are optional. If a product pricing slide uses formula variables that are not supplied, the price is deferred — the slide displays a placeholder instead of failing. Deferred prices can be resolved later when the missing values become available (e.g., after roof measurements). AI Presentations defers a measurement-backed variable only when a formula on one of the deck’s product-pricing slides references it. Price variables left in the deck’s settings after their product slide was removed, or after its formula stopped referencing them, are ignored. While the deck itself is still being generated, the request is refused with 409 DECK_GENERATING: a presentation is a permanent snapshot of the deck at creation time, so one taken mid-generation would be permanently missing slides. Retry once the generation job completes.
curl --request POST \
--url https://app.demand-iq.com/api/decks/{deckId}/presentations \
--header 'Content-Type: application/json' \
--cookie session= \
--data '
{
"prospect_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"voice_settings": {},
"language_settings": {},
"price_inputs": {},
"measurement_strategy": "manual"
}
'import requests
url = "https://app.demand-iq.com/api/decks/{deckId}/presentations"
payload = {
"prospect_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"voice_settings": {},
"language_settings": {},
"price_inputs": {},
"measurement_strategy": "manual"
}
headers = {
"cookie": "session=",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {cookie: 'session=', 'Content-Type': 'application/json'},
body: JSON.stringify({
prospect_id: '3c90c3cc-0d44-4b50-8888-8dd25736052a',
voice_settings: {},
language_settings: {},
price_inputs: {},
measurement_strategy: 'manual'
})
};
fetch('https://app.demand-iq.com/api/decks/{deckId}/presentations', 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://app.demand-iq.com/api/decks/{deckId}/presentations",
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([
'prospect_id' => '3c90c3cc-0d44-4b50-8888-8dd25736052a',
'voice_settings' => [
],
'language_settings' => [
],
'price_inputs' => [
],
'measurement_strategy' => 'manual'
]),
CURLOPT_COOKIE => "session=",
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$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://app.demand-iq.com/api/decks/{deckId}/presentations"
payload := strings.NewReader("{\n \"prospect_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"voice_settings\": {},\n \"language_settings\": {},\n \"price_inputs\": {},\n \"measurement_strategy\": \"manual\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("cookie", "session=")
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://app.demand-iq.com/api/decks/{deckId}/presentations")
.header("cookie", "session=")
.header("Content-Type", "application/json")
.body("{\n \"prospect_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"voice_settings\": {},\n \"language_settings\": {},\n \"price_inputs\": {},\n \"measurement_strategy\": \"manual\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.demand-iq.com/api/decks/{deckId}/presentations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["cookie"] = 'session='
request["Content-Type"] = 'application/json'
request.body = "{\n \"prospect_id\": \"3c90c3cc-0d44-4b50-8888-8dd25736052a\",\n \"voice_settings\": {},\n \"language_settings\": {},\n \"price_inputs\": {},\n \"measurement_strategy\": \"manual\"\n}"
response = http.request(request)
puts response.read_body{
"deck_presentation_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"url": "<string>",
"status": "not_started"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>"
}{
"error": "<string>",
"code": "DECK_GENERATING"
}{
"error": "<string>",
"code": "INVALID_ADDRESS"
}{
"error": "<string>"
}{
"error": "<string>"
}Authorizations
Session cookie obtained from POST /api/auth/login.
Path Parameters
UUID of the deck
Body
Show child attributes
Show child attributes
Prospect to attach the presentation to. Alone, homeowner data is fetched from the prospect's journey; with homeowner, the prospect is attached and the supplied data is used as-is. Must be visible to your credential.
Values for deck-level price variables (used to evaluate formulas). Optional — omitted variables result in deferred pricing rather than an error.
Show child attributes
Show child attributes
Set to auto to resolve measurement-backed price variables from automated roof measurements instead of supplying them in price_inputs. Only variables referenced by a formula on the deck's product-pricing slides are deferred to measurements.
manual, auto