curl --request POST \
--url https://api.instaview.sk/agents/composer \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"message": "Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.",
"type": "PHONE",
"name": "Senior Developer Phone Screen",
"duration": 90.5,
"voiceId": "<string>",
"companyPhoneNumberId": "<string>",
"metadata": {}
}
EOFimport requests
url = "https://api.instaview.sk/agents/composer"
payload = {
"message": "Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.",
"type": "PHONE",
"name": "Senior Developer Phone Screen",
"duration": 90.5,
"voiceId": "<string>",
"companyPhoneNumberId": "<string>",
"metadata": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
message: 'Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they\'re a strong fit.',
type: 'PHONE',
name: 'Senior Developer Phone Screen',
duration: 90.5,
voiceId: '<string>',
companyPhoneNumberId: '<string>',
metadata: {}
})
};
fetch('https://api.instaview.sk/agents/composer', 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.instaview.sk/agents/composer",
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([
'message' => 'Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they\'re a strong fit.',
'type' => 'PHONE',
'name' => 'Senior Developer Phone Screen',
'duration' => 90.5,
'voiceId' => '<string>',
'companyPhoneNumberId' => '<string>',
'metadata' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://api.instaview.sk/agents/composer"
payload := strings.NewReader("{\n \"message\": \"Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.\",\n \"type\": \"PHONE\",\n \"name\": \"Senior Developer Phone Screen\",\n \"duration\": 90.5,\n \"voiceId\": \"<string>\",\n \"companyPhoneNumberId\": \"<string>\",\n \"metadata\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.instaview.sk/agents/composer")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": \"Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.\",\n \"type\": \"PHONE\",\n \"name\": \"Senior Developer Phone Screen\",\n \"duration\": 90.5,\n \"voiceId\": \"<string>\",\n \"companyPhoneNumberId\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.instaview.sk/agents/composer")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"message\": \"Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.\",\n \"type\": \"PHONE\",\n \"name\": \"Senior Developer Phone Screen\",\n \"duration\": 90.5,\n \"voiceId\": \"<string>\",\n \"companyPhoneNumberId\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_body{
"sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agent": {
"type": "ONLINE",
"flow": {
"firstMessage": {
"type": "first_message",
"text": "Hi {{contact.firstName}}, do you have two minutes?",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "Qualify budget"
},
"sections": [
{
"type": "sequential",
"prompt": "Introduce yourself and explain why you are calling.",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "Qualify budget"
}
],
"lastMessage": {
"type": "last_message",
"text": "Thanks for your time — have a great day!",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "Qualify budget"
},
"schemaVersion": "1.0.0"
},
"name": "Senior Developer Phone Screen",
"language": "EN",
"duration": 123,
"voiceId": "<string>",
"backgroundSound": "OFFICE",
"companyPhoneNumberId": "<string>",
"metadata": {},
"overrides": {
"companyName": "Acme Manufacturing",
"companyDescription": "Acme makes industrial fasteners and employs 400 people across Slovakia."
},
"guardrails": {
"rules": [
"Never quote a price",
"Never promise a delivery date"
]
},
"contextConfig": {
"role": "an account executive for Acme",
"communicationStyle": "concise and direct",
"callToAction": "book a 30-minute demo",
"useContactContext": true
},
"analyticsConfig": {
"extractionTargets": [
{
"label": "Budget confirmed",
"type": "bool",
"key": "budget_confirmed",
"enumValues": [
"hot",
"warm",
"cold"
],
"ideal": "A confirmed budget of at least 10k",
"importance": "PREFERRED",
"weight": 40
}
],
"scoringCriteria": [
{
"label": "Handled objections",
"description": "Acknowledged the objection and answered it with a concrete example",
"key": "handled_objections",
"importance": "PREFERRED",
"weight": 40
}
],
"outcomes": [
{
"label": "Meeting booked",
"description": "The contact agreed to a specific date and time",
"key": "meeting_booked"
}
],
"outputTags": [
{
"label": "Needs follow-up",
"key": "needs_followup",
"description": "The contact asked to be called back"
}
],
"capture": {
"recording": true,
"transcript": true,
"summary": true,
"qa": true,
"evaluation": true,
"sentiment": true
}
},
"callConfig": {
"retryPolicy": {
"maxAttempts": 3
},
"callWindow": {
"days": [
"mon",
"tue",
"wed",
"thu",
"fri"
],
"start": "09:00",
"end": "18:00",
"timezoneAnchor": {
"mode": "contact",
"timezone": "Europe/Bratislava",
"fallbackTimezone": "Europe/Bratislava"
}
}
}
},
"message": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"role": "user",
"content": "Your agent opens by introducing itself and confirming it's speaking with the right person. It then works through Go and Postgres experience…",
"createdAt": "2023-11-07T05:31:56Z",
"changes": [
"Added a relocation question",
"Set the language to Slovak"
]
},
"expiresAt": "2023-11-07T05:31:56Z"
}{
"statusCode": 422,
"message": "Invalid conversation flow.",
"errors": [
{
"code": "UNKNOWN_BLOCK_TYPE",
"message": "Unknown block type: teleport.",
"path": "sections[2].branches[0].blocks[1]"
}
],
"traceId": "6a707aee000000000c1e285eefed9980"
}Design an Agent
Designs a complete custom agent from a plain-language brief and returns it as a preview, together with a session you can keep refining. No agent is created. Nothing appears in your agent list until you post the returned agent (or { "composerSessionId": … }) to POST /agents — the 201 here created a session, not an agent.
curl --request POST \
--url https://api.instaview.sk/agents/composer \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data @- <<EOF
{
"message": "Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.",
"type": "PHONE",
"name": "Senior Developer Phone Screen",
"duration": 90.5,
"voiceId": "<string>",
"companyPhoneNumberId": "<string>",
"metadata": {}
}
EOFimport requests
url = "https://api.instaview.sk/agents/composer"
payload = {
"message": "Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.",
"type": "PHONE",
"name": "Senior Developer Phone Screen",
"duration": 90.5,
"voiceId": "<string>",
"companyPhoneNumberId": "<string>",
"metadata": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
message: 'Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they\'re a strong fit.',
type: 'PHONE',
name: 'Senior Developer Phone Screen',
duration: 90.5,
voiceId: '<string>',
companyPhoneNumberId: '<string>',
metadata: {}
})
};
fetch('https://api.instaview.sk/agents/composer', 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.instaview.sk/agents/composer",
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([
'message' => 'Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they\'re a strong fit.',
'type' => 'PHONE',
'name' => 'Senior Developer Phone Screen',
'duration' => 90.5,
'voiceId' => '<string>',
'companyPhoneNumberId' => '<string>',
'metadata' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"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://api.instaview.sk/agents/composer"
payload := strings.NewReader("{\n \"message\": \"Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.\",\n \"type\": \"PHONE\",\n \"name\": \"Senior Developer Phone Screen\",\n \"duration\": 90.5,\n \"voiceId\": \"<string>\",\n \"companyPhoneNumberId\": \"<string>\",\n \"metadata\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.instaview.sk/agents/composer")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"message\": \"Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.\",\n \"type\": \"PHONE\",\n \"name\": \"Senior Developer Phone Screen\",\n \"duration\": 90.5,\n \"voiceId\": \"<string>\",\n \"companyPhoneNumberId\": \"<string>\",\n \"metadata\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.instaview.sk/agents/composer")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"message\": \"Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit.\",\n \"type\": \"PHONE\",\n \"name\": \"Senior Developer Phone Screen\",\n \"duration\": 90.5,\n \"voiceId\": \"<string>\",\n \"companyPhoneNumberId\": \"<string>\",\n \"metadata\": {}\n}"
response = http.request(request)
puts response.read_body{
"sessionId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agent": {
"type": "ONLINE",
"flow": {
"firstMessage": {
"type": "first_message",
"text": "Hi {{contact.firstName}}, do you have two minutes?",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "Qualify budget"
},
"sections": [
{
"type": "sequential",
"prompt": "Introduce yourself and explain why you are calling.",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "Qualify budget"
}
],
"lastMessage": {
"type": "last_message",
"text": "Thanks for your time — have a great day!",
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "Qualify budget"
},
"schemaVersion": "1.0.0"
},
"name": "Senior Developer Phone Screen",
"language": "EN",
"duration": 123,
"voiceId": "<string>",
"backgroundSound": "OFFICE",
"companyPhoneNumberId": "<string>",
"metadata": {},
"overrides": {
"companyName": "Acme Manufacturing",
"companyDescription": "Acme makes industrial fasteners and employs 400 people across Slovakia."
},
"guardrails": {
"rules": [
"Never quote a price",
"Never promise a delivery date"
]
},
"contextConfig": {
"role": "an account executive for Acme",
"communicationStyle": "concise and direct",
"callToAction": "book a 30-minute demo",
"useContactContext": true
},
"analyticsConfig": {
"extractionTargets": [
{
"label": "Budget confirmed",
"type": "bool",
"key": "budget_confirmed",
"enumValues": [
"hot",
"warm",
"cold"
],
"ideal": "A confirmed budget of at least 10k",
"importance": "PREFERRED",
"weight": 40
}
],
"scoringCriteria": [
{
"label": "Handled objections",
"description": "Acknowledged the objection and answered it with a concrete example",
"key": "handled_objections",
"importance": "PREFERRED",
"weight": 40
}
],
"outcomes": [
{
"label": "Meeting booked",
"description": "The contact agreed to a specific date and time",
"key": "meeting_booked"
}
],
"outputTags": [
{
"label": "Needs follow-up",
"key": "needs_followup",
"description": "The contact asked to be called back"
}
],
"capture": {
"recording": true,
"transcript": true,
"summary": true,
"qa": true,
"evaluation": true,
"sentiment": true
}
},
"callConfig": {
"retryPolicy": {
"maxAttempts": 3
},
"callWindow": {
"days": [
"mon",
"tue",
"wed",
"thu",
"fri"
],
"start": "09:00",
"end": "18:00",
"timezoneAnchor": {
"mode": "contact",
"timezone": "Europe/Bratislava",
"fallbackTimezone": "Europe/Bratislava"
}
}
}
},
"message": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"role": "user",
"content": "Your agent opens by introducing itself and confirming it's speaking with the right person. It then works through Go and Postgres experience…",
"createdAt": "2023-11-07T05:31:56Z",
"changes": [
"Added a relocation question",
"Set the language to Slovak"
]
},
"expiresAt": "2023-11-07T05:31:56Z"
}{
"statusCode": 422,
"message": "Invalid conversation flow.",
"errors": [
{
"code": "UNKNOWN_BLOCK_TYPE",
"message": "Unknown block type: teleport.",
"path": "sections[2].branches[0].blocks[1]"
}
],
"traceId": "6a707aee000000000c1e285eefed9980"
}POST /agents. The 201 returned here created a
composer session, not an agent.The shortest useful call
POST /agents/composer
{
"type": "PHONE",
"message": "Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit."
}
{
"sessionId": "9f8c1d2e-4b7a-4c3d-9e1f-2a5b6c7d8e90",
"agent": {
"name": "Senior Backend Phone Screen",
"type": "PHONE",
"language": "EN",
"flow": { "firstMessage": { "…": "…" }, "sections": [], "lastMessage": { "…": "…" } },
"contextConfig": { "…": "…" },
"analyticsConfig": { "…": "…" }
},
"message": {
"id": "…",
"role": "assistant",
"content": "Your agent opens by introducing itself and confirming it's speaking with the right person. It then works through Go and Postgres experience, asks about notice period and salary expectation, and transfers strong candidates to a recruiter. Anyone who isn't a fit gets a polite close.",
"changes": ["Named it Senior Backend Phone Screen", "Set the language to EN", "Added Skills deep-dive"],
"createdAt": "2026-08-18T10:15:00.000Z"
},
"expiresAt": "2026-09-17T10:15:00.000Z"
}
message, and the type of agent to design.
type is required and cannot change later. The channel decides which conversation
steps are legal at all — a transfer to a person exists only on PHONE — so it has to be
known before the agent is designed rather than guessed from your brief.name, language, duration, voiceId, backgroundSound, companyPhoneNumberId and metadata are for: pin the things you already know, and let the composer decide the rest.
callConfig — already in the shape
POST /agents accepts, so it survives whether you post
the preview back or just the composerSessionId. It is not a field of this route: to set one
exactly rather than describe it, send callConfig in your create call, where it wins over
whatever the composer chose. When neither of you says anything, a new agent calls Mon–Fri
09:00–18:00 in each contact’s own timezone.flow. flow, focus, guardrails, contextConfig and
analyticsConfig are what the composer writes for you, and sending any of them is
rejected with a 422 naming which. If you already have a flow, you do not need the
composer — post it straight to POST /agents.The accompanying message
message.content is a plain-language description of the agent, in at most ten sentences: what it opens with, what it asks about, what it decides, and how it ends. It is written for a person, not for a parser — it is the text to show next to a “create this agent” button.
message.changes is a short list of labels for what the composer did. On the first turn it describes what it built; on later turns, what changed.
What to do with the preview
agent is a body POST /agents accepts verbatim — no ids, no schemaVersion, and a conditional’s fallthrough already named by defaultBranch. Nothing to strip, nothing to fix up.
Change something first
Create it
agent object, or just { "composerSessionId": "…" }.POST /agents
{ "composerSessionId": "9f8c1d2e-4b7a-4c3d-9e1f-2a5b6c7d8e90" }
{ "composerSessionId": "…", "name": "Backend Screen v2" } creates the previewed agent under a different name.
Sessions expire
A session lives 30 days. After that, a preview nobody accepted is deleted along with its conversation;expiresAt on every response tells you when. Creating the agent ends the clock — a session that became an agent is kept.
Rate limits
Every call here designs an agent with a language model, so these routes carry their own limit on top of your key’s usual allowance: 6 per minute and 60 per hour for this endpoint, 10 per minute and 120 per hour for refining. Exceeding it is a429.
Errors
| Status | When |
|---|---|
400 | message or type missing, malformed, or over 8,000 characters — or you sent flow, focus, guardrails, contextConfig or analyticsConfig, which the composer writes (property flow should not exist) |
404 | The composer is not enabled for your account |
422 | The brief could not be turned into a valid agent, or you sent flow / focus / a config object |
429 | Composer rate limit reached |
503 | The model was unavailable or timed out — nothing was created |
422 here means no session was created. Rephrase or add detail and call again.
Authorizations
API key for authentication using Bearer scheme
Query Parameters
Required for ATS API keys to specify which company to access. Ignored for standard company API keys.
Body
What the agent should do, in plain language. The composer designs the whole conversation from this.
8000"Screen senior backend devs. Ask about Go and Postgres, check notice period and salary expectation, then hand off to a recruiter if they're a strong fit."
The channel the agent will run on. Required, and fixed for the life of the session: it decides which conversation steps are available at all — a transfer to a person exists only on PHONE.
ONLINE, PHONE "PHONE"
Name for the agent. Optional — the composer proposes one from your brief. Anything you send here wins, and keeps winning on later turns.
3 - 100"Senior Developer Phone Screen"
Language of the conversation. When omitted, the composer follows the language of your brief.
EN, SK, CS Target call length in minutes.
1 <= x <= 180Voice for the agent.
Ambient background sound.
OFFICE, OFF The number this agent should call from. PHONE agents only.
Custom metadata carried through to the agent you create from this session.
Response
The composer session, the agent it would create, and the composer's reply.
The session to send follow-up messages to, and to create the agent from.
The agent the composer would build. There is no id: this agent does not exist yet. Post this object to POST /agents to create it — it is accepted verbatim, with no ids or schemaVersion to strip first.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
When this session is deleted if you never create the agent. Sessions live 30 days.